TypeScriptにおけるvoidとneverは異なる型です:
例:
function log(message: string): void { console.log(message); } function throwError(message: string): never { throw new Error(message); } function infinite(): never { while (true) {} }
neverは、型に基づくswitch-caseの完全性の制御にしばしば使用されます:
type Shape = { kind: 'circle' } | { kind: 'square' }; function getArea(shape: Shape) { switch (shape.kind) { case 'circle': return 1; case 'square': return 2; default: const _exhaustive: never = shape; // ここでコンパイラはケースが見逃された場合を指摘します } }
voidとneverは、関数の戻り値の型として相互に使われることができますか?
答え: いいえ。void型の関数はundefinedを返すか、何も返さずに実行を終了しますが、never型の関数は決して実行を正常に終了しません(エラーを投げるか無限ループに入ります)。
例:
const fn: () => never = () => { // return; // エラー:宣言された型が'never'である関数は返されてはいけません。 };
歴史
分析システムでは、常に例外をスローするバリデータ関数を戻り値がvoidとしてタイプ指定しました。その結果、開発者は関数が「戻る」ことができると思い込み、さらなるチェックを行わなかったため、エラーハンドリングを見逃し、呼び出しチェーンを誤って実装しました。
歴史
あるプロジェクトでは、厳密にタイプ指定された戻り値の型を期待するTypeScript関数をvoidではなくneverとして定義しました。そのため、neverとしてタイプ指定されたコールバック関数を持つ関数を呼び出そうとすると、コンパイルエラーやAPI互換性の喪失が発生しました。
歴史
バックエンドモジュールでは、以前はvoidで戻される関数を介して例外をスローしていました。TypeScriptに移行した後、この関数の呼び出し後のコードの範囲外に出ることについて警告しなかったため、余計な「到達不可能な」コードの行が発生しました。そして、戻り値の型をneverに変更することで、そういった箇所を発見し、コードをクリーンにし、論理エラーを防ぎました。