TypeScriptのNever型をまとめる

## はじめに

neverって何？どこで使うの？ってことで調べたのでまとめます。

## never型とは

> never型は「値を持たない」を意味するTypeScriptの特別な型です。
[サバイバルTypeScript](https://typescriptbook.jp/reference/statements/never)

つまり、
**どんな値も持たない＝どんな変数も代入することができない**
ということです。

## それvoid型と一緒じゃないん？

そう、私のようにvoid型と一緒じゃないか？と疑問を持つ方がいるかもしれません...
**結論から言うとvoid型とは異なります。**（それはそうやろ）

ということでvoid型との違いをまとめてみました。

### void型
- 処理が正常終了した結果、何も返さない（最後まで実行される）
- undefinedを代入できる

```void.ts
// OK
const sampleFunc1 = (): void => {
  return undefined;
}
```

### never型
- 処理が中断されるか、永遠に実行される（最後まで実行されない）
- つまり、そもそも関数が正常終了しないので値が返ってこない
- undefinedすら代入できない（何も代入できないので）


```never.ts
// NG: never型にundefinedを代入できない
const sampleFunc2 = (): never => {
  return undefined;
}
// Type 'undefined' is not assignable to type 'never'.

// OK: エラーを投げるだけの関数（最後まで到達しない）
const sampleFunc3 = (): never => {
  throw new Error('Errorをthrowします')
  // ここに到達できない
}

// OK: 永遠に実行される関数
const sampleFunc4 = (): never => {
  while(true) {
    console.log('永遠にループするで')
  }
  // ここに到達できない
}

```

never型の例として、「エラーを投げるだけの関数」と「永遠に実行される関数」の2つがよく紹介されているけれど、こんなん使うのか？？？と思う。後者に至ってはただのバグじゃないかと。。。

ちなみに「分岐の結果、エラーを投げる関数」は、never型にできません。

```ts
// NG: 分岐の結果、エラーを投げる関数
const sampleFunc5 = (err: boolean): never => {
  if(err) {
    throw new Error('Errorをthrowします')
  }
}
// A function returning 'never' cannot have a reachable end point.
// 略: never型を返す関数は、最後まで到達できてはいけないよ〜

```

「`err`が`false`の場合は、エラーを投げずに最後まで到達してしまうので、never型にはできないよ」という意味ですね。

:::note info
never型とvoid型は異なる
:::

## 結局いつ使えるか？

上にあげた関数たちはとても実用性がなさそうです。

1つ言うなら、推論の結果、`never`を返す関数があったら、最後まで到達できない関数（＝つまりバグ？）を検知できそうってくらいかなぁと...（見たことないですが）

ただ、このneverの特性を活かし、唯一使えそうなのがswitch文における**「網羅性チェック」**です。

### 網羅性チェック

例えば、以下のようなユニオンリテラル型である`Animal`を受け取り、鳴き声を出力する関数(`cryFunc`)があるとします。

`cryFunc`内のcase句で、`Animal`に定義されている全てのリテラルを網羅したい所ですが、現状`Bird`のcase句が足りていません。
**このことをTypeScriptはエラーで教えてくれません;;**


```ts
type Animal = 'Dog' | 'Cat' | 'Bird';

const cryFunc = (animal: Animal) => {
  switch(animal) {
    case 'Dog':
      console.log('ワン！')
      break;
    case 'Cat':
      console.log('ニャー！')
      break;
    // 'Bird'のcase句が存在しないけど、エラーが発生しない
  }
}
```

これの何が困るかと言うと、`cryFunc`の存在を知らない開発者が`Animal`に値を追加した時に、コンパイルエラーが出ないので、バグを生んでしまう可能性があることです。

ここでエラーを出すようにするために登場するのが、`never`です！

```ts
type Animal = 'Dog' | 'Cat' | 'Bird';

const cry = (animal: Animal) => {
  switch(animal) {
    case 'Dog':
      console.log('ワン！')
      break;
    case 'Cat':
      console.log('ニャー！')
      break;
    default:
      const exhaustivenessCheck: never = animal;
      // NG: Type 'string' is not assignable to type 'never'.
      break;
  }
}
```

default句の中で、網羅性をチェックしたい値に対して、never型を定義してあげます。
こうすることで、TypeScriptが代入に関するエラーを出してくれるようになります。

※網羅性チェック用の例外クラスを作ると良いらしい。
参考：[例外による網羅性チェック
](https://typescriptbook.jp/reference/statements/never#%E4%BE%8B%E5%A4%96%E3%81%AB%E3%82%88%E3%82%8B%E7%B6%B2%E7%BE%85%E6%80%A7%E3%83%81%E3%82%A7%E3%83%83%E3%82%AF)

:::note info
never型は網羅性チェックとしてだけは使えそう
:::


## 結論

never型とは？

- 何も代入ができない
- void型とは異なる
- 網羅性チェックをしたいシーンでは活躍しそう（それ以外は...）

## 参考にさせて頂いた記事

[サバイバルTypeScript](https://typescriptbook.jp/reference/statements/never)
[TypeScript Deep Dive 日本語版](https://typescript-jp.gitbook.io/deep-dive/type-system/never)
[TypeScriptのNever型とは？
](https://qiita.com/macololidoll/items/1c948c1f1acb4db6459e)