LoginSignup
0
0

TypeScript の never型

Last updated at Posted at 2023-06-26

TypeScriptのnever型についてまとめます。
今まではいまいち使い所が分かっていないな〜という感じでしたが、今回理解できた気がします。

動作確認環境

never型とは

TypeScriptの型の一つです。
「値を持たない」ことを表します。

never型の特徴

関数の戻り値型で考えてみます。何も値を返さない関数の場合、戻り値型がneverとなります。
例えば終了しない関数や、必ず例外が発生する関数は戻り値の型がneverとなります。

終了しない関数

const fn = (): never => {
    while (true) {}; // 無限ループ
}

必ず例外が発生する関数

const fn = (): never => {
    throw new Error();
}

never型の変数にはnever型の値のみ代入できます。その他の値は代入できません。

const foo: never = 1; //  Error : Type 'number' is not assignable to type 'never'.

const fn = (): never => {
    throw new Error();
}
const bar: never = fn();

never型はどんな型にも代入できます。

const fn = (): never => {
    throw new Error();
}
const num: number = fn();
const str: string = fn();
const flag: boolean = fn();

また、「作り得ない型」もnever型となります。

// numberとstringを同時に満たす値は作り得ない。NumberStringはneverとなる
type NumberString = number & string; 

never と void の違い

void は値を返さない関数の戻り値として使われます。
※正確には、関数が何も返さないことを強制するものではなはなく、他の値を返すことができますが無視されます。詳しくはこちらを参照してください。

言い換えると、値を何も返さない関数はvoidを返します。returnを返すことがない(または常にスローする)関数はneverを返します

次のような場合はvoidを返すのが正しいので、TypeScriptはコンパイルエラーを出します。

// Error : A function returning 'never' cannot have a reachable end point.
const fn = (value: string): never => { 
  console.log(value)
}

never型の使い所 網羅チェック

never型を使うことで、網羅チェックを行い、網羅できていない場合に(実行時エラーではなく)コンパイルエラーを出すことができます。

以下のコードを例にします。

type FavoriteFruit = "Apple" | "Lemon";

const getMainColor = (fruit: FavoriteFruit) => {
  switch (fruit) {
    case "Apple":
      return "red";
    case "Lemon":
      return "yellow";
    default:
      throw new Error(`${fruit} is not in FavoriteFruit.`);
  }
} 

console.log(getMainColor("Apple")); // "red"

FavoriteFruit に Grape「葡萄」を追加することにしました。

type FavoriteFruit = "Apple" | "Lemon" | "Grape";

関数getMainColorGrapeに対応する値を返すように修正する必要があります。
が、getMainColorへの修正を忘れてしまったとします。

getMainColorへの修正をしない状態でgetMainColor("Grape")というコードを書いてもコンパイルエラーにはなりません。
実行することによって Grape is not in FavoriteFruit. のようにエラーが発生します。

type FavoriteFruit = "Apple" | "Lemon" | "Grape";

const getMainColor = (fruit: FavoriteFruit) => {
  switch (fruit) {
    case "Apple":
      return "red";
    case "Lemon":
      return "yellow";
    default:
      throw new Error(`${fruit} is not in FavoriteFruit.`);
  }
} 

console.log(getMainColor("Grape")); // Error : Grape is not in FavoriteFruit. 

getMainColornever型を使う実装を行うことによって、これをコンパイルエラーとして検知することができるようになります。

const getMainColor = (fruit: FavoriteFruit) => {
  switch (fruit) {
    case "Apple":
      return "red";
    case "Lemon":
      return "yellow";
    default:
      const _check: never = fruit; // Type 'string' is not assignable to type 'never'.
      return _check
  }
} 

never型の変数にはnever型以外の値を代入することができません。
fruit変数の値がGrapeだった場合、default分岐に入ってくるため、それを推測したTypeScriptがコンパイルエラーを出しています。コンパイルエラーによって、実行前にコードを修正することができます。

以下が修正したコードです。

type FavoriteFruit = "Apple" | "Lemon" | "Grape";

const getMainColor = (fruit: FavoriteFruit) => {
  switch (fruit) {
    case "Apple":
      return "red";
    case "Lemon":
      return "yellow";
    case "Grape":
      return "violet";
    default:
      const _check: never = fruit;
      return _check
  }
} 
 
console.log(getMainColor("Grape")); // "violet"

参考

0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0