assertNever 網羅性チェック
最終更新:2026-10-22
assertNever 網羅性チェック
網羅性チェックは「ユニオン型のすべてのメンバーが処理されていること」を保証するコンパイル時の保険です。中核は assertNever の技法です。switch の default 分岐でパラメータを never 型として宣言します。メンバーを見落としていれば、そのメンバーはまだ流れ込めるので引数は never にならず、コンパイラが即座にエラーを出します。
| グループ | ナローイングとアサーション |
|---|---|
| 種類 | 構文 |
| strict モード | 不要 |
ℹ️ 説明 エディタは TypeScript を JavaScript にトランスパイルして実行します(型チェックなし)。型チェック結果は各サンプルの出力を参照してください。
📝 構文
function assertNever(x: never): never {
throw new Error("Unhandled: " + JSON.stringify(x));
}
function area(shape: Shape): number {
switch (shape.kind) {
case "circle": return Math.PI * shape.radius ** 2;
case "square": return shape.side ** 2;
default:
return assertNever(shape); // 分岐の処理漏れがあるとここでコンパイルエラーになる
}
}
⚙️ パラメータ
| x: never | never はすべての型のサブタイプであり、メンバーを持ちません。すべての分岐が処理され残りの型が空になったときにのみ、実引数は never を満たします |
|---|
戻り値:assertNever は never を返します(正常には返らず、実行時には例外を投げます)。コンパイル時の「戻り値」はエラーメッセージ "Argument of type 'X' is not assignable to parameter of type 'never'" です。
▶ サンプル
下のコードを編集して「試す」を押すと、トランスパイル実行結果を確認できます:
出力:
3.14
9
(トランスパイル後の実行出力。現状は分岐がすべて揃っているため、assertNever には到達しません。Shape に kind: "triangle" を追加して分岐を足さなければ、tsc は default 行で "Argument of type '{ kind: \"triangle\"; ... }' is not assignable to parameter of type 'never'" を報告します)
💡 ポイント
ℹ️ 説明 仕組みはこうです。TS はユニオンへの switch で、処理済みのメンバーを型から取り除きます。すべてのメンバーを処理し終えると shape は never になり、never のパラメータへ代入できます。1つでも見落としがあれば代入は失敗し、エラー位置はまさに default 行です。
💡 ヒント ユニオンに新しいメンバーを追加するときが、網羅性チェックが最も価値を発揮する瞬間です。呼び出し箇所を全域検索する必要はなく、コンパイラが分岐漏れの switch をすべて一覧で示してくれます。
⚠️ 注意 網羅性チェックは判別可能ユニオンに依存します。判別フィールドはリテラル型でなければなりません。string 型の判別フィールドを switch したり if-else 連鎖を使ったりすると、コンパイラは網羅性を判断できません。
🏷️ 関連ナローイングとアサーション
💡 関連ケース
ケースは今後追加予定です。
📚 関連チュートリアル
❓ よくある質問
Q assertNever を使わずに switch を全部書けば済むのでは?
A TS の noImplicitReturns / strict モードは「戻り値のないパスがある」ことは検出できますが、「論理的に1つのケースの処理漏れがある」(たとえばある分岐が黙ってデフォルト値を返す)ことは検出できません。assertNever は「処理漏れ」をハードなコンパイルエラーに変えます。
Q if-else 連鎖でも網羅性チェックはできますか?
A できます。最後の else で assertNever(x) を呼べばよく、仕組みは同じです。ただし switch は判別可能ユニオンに対する可読性も網羅性の分析もしやすいので、switch をおすすめします。
Q 実行時に assertNever が実行されることはありますか?
A 正しく保守されているコードではありません。実行されるのは「コンパイル時にすでに防がれている」分岐漏れの場面だけです。throw を残しておくのは防御的プログラミングです。万が一実行時のデータが型の宣言と食い違った場合(古いキャッシュデータなど)、黙って誤った分岐を通るより早く露見します。