never 型が必要になる場面
never は「絶対に起こらない」ことを表す型だ。一見すると使い道がなさそうだが、実際には型の網羅性チェックやエラー処理で重要な役割を果たす。
never 型とは何か
never は「値が存在しない」ことを示す型である。関数が正常に終了しない場合や、論理的にあり得ない状態を表現するために使う。
// 例外を投げる関数は never を返す
function throwError(message: string): never {
throw new Error(message);
}
// 無限ループも never を返す
function infiniteLoop(): never {
while (true) {}
}void と混同しやすいが、意味がまったく違う。void は「戻り値がない」、never は「そもそも戻ってこない」である。
関数が正常に終了するが、戻り値がない。return; や return undefined; で終わる関数。
関数が正常に終了しない。例外を投げるか、無限ループで終わらない関数。
網羅性チェックでの活用
never の最も実用的な使い方は、switch 文の網羅性チェックだ。
type Status = "pending" | "approved" | "rejected";
function handleStatus(status: Status): string {
switch (status) {
case "pending":
return "処理中";
case "approved":
return "承認済み";
case "rejected":
return "却下";
default:
// ここに到達するはずがない
const _exhaustive: never = status;
return _exhaustive;
}
}すべてのケースを処理していれば、default に到達することはない。つまり status は never 型になる。
ここで威力を発揮するのは、Status に新しい値が追加されたときだ。
type Status = "pending" | "approved" | "rejected" | "cancelled"; // 追加
function handleStatus(status: Status): string {
switch (status) {
case "pending":
return "処理中";
case "approved":
return "承認済み";
case "rejected":
return "却下";
default:
// エラー: Type 'string' is not assignable to type 'never'
const _exhaustive: never = status;
return _exhaustive;
}
}"cancelled" を処理していないため、default 節で status は "cancelled" 型になる。これを never に代入しようとするとコンパイルエラーになり、処理漏れを検出できる。
条件型での never
条件型でフィルタリングを行うとき、never が活躍する。
type NonNullable<T> = T extends null | undefined ? never : T;
type A = NonNullable<string | null>; // string
type B = NonNullable<number | undefined>; // numberユニオン型から特定の型を除外したいとき、該当する型を never に変換する。never はユニオン型に含まれていても無視されるため、結果的にフィルタリングが実現する。
// string | never は string と同じ
type Test = string | never; // string関数オーバーロードでの never
あり得ない引数の組み合わせを禁止するためにも使える。
function process(value: string): string;
function process(value: number): number;
function process(value: string | number): string | number {
if (typeof value === "string") {
return value.toUpperCase();
}
return value * 2;
}
// boolean を渡すとエラー
// process(true);型システムが「この組み合わせは存在しない」と判断した結果が never として現れる。
実践的なパターン
asserts キーワードと組み合わせて、値の検証を行う関数を書ける。検証に失敗したら例外を投げ、never を返す。
回復不能なエラーを投げる関数の戻り値として使う。呼び出し元に「この関数は戻ってこない」と伝えられる。
never は TypeScript の型システムにおける「底」の型だ。すべての型のサブタイプであり、どんな型にも代入できるが、逆に never には何も代入できない(never 自身を除く)。この特性を理解しておくと、高度な型パズルも読み解けるようになる。