📚

TypeScriptのassertNeverで型安全性を極める:switch文の完全ガイド

2024/10/19に公開

はじめに

TypeScriptを使う上で、型安全性は常に重要なトピックです。特に、列挙型(enum)やユニオン型を扱うswitch文では、すべてのケースを漏れなく処理することが求められます。この記事では、assertNever関数を使って型安全性を高める方法を、switch文を中心に詳しく解説します。

assertNeverとは

assertNeverは、コンパイル時に到達してはいけないコードパスを明示的に示すための関数です。基本的な実装は以下のとおりです。

function assertNever(x: never): never {
    throw new Error("Unexpected object: " + x);
}

この関数は、never型の引数を受け取り、常に例外をスローします。TypeScriptの型システムにおいて、never型は「決して発生しない」値の型を表します。

switch文での使用例

交通信号の色を表す列挙型を例に、assertNeverの使用方法を見ていきましょう。

enum TrafficLight {
    Red,
    Yellow,
    Green
}

パターン1: assertNeverを使用する場合

function getAction(light: TrafficLight): string {
    switch (light) {
        case TrafficLight.Red:
            return "Stop";
        case TrafficLight.Yellow:
            return "Caution";
        case TrafficLight.Green:
            return "Go";
        default:
            return assertNever(light);
    }
}

このパターンでは、すべてのケースが処理されていることが保証されます。新しい値がTrafficLightに追加された場合、コンパイラがエラーを報告します。

パターン2: defaultを書く場合

function getAction(light: TrafficLight): string {
    switch (light) {
        case TrafficLight.Red:
            return "Stop";
        case TrafficLight.Yellow:
            return "Caution";
        case TrafficLight.Green:
            return "Go";
        default:
            return "Unknown action";
    }
}

このパターンでは、コンパイルエラーは発生しませんが、新しい値がTrafficLightに追加された際に気づきにくくなります。

パターン3: defaultを書かない場合

function getAction(light: TrafficLight): string {
    switch (light) {
        case TrafficLight.Red:
            return "Stop";
        case TrafficLight.Yellow:
            return "Caution";
        case TrafficLight.Green:
            return "Go";
    }
}

このパターンでは、TypeScriptの設定によって動作が異なります:

  1. --strictNullChecksが有効な場合:コンパイラは、関数がundefinedを返す可能性があることを警告します。
  2. --strictNullChecksが無効な場合:警告なしでundefinedが返される可能性があります。

各パターンの比較

特性 assertNever defaultあり defaultなし
型安全性 中(設定依存)
コード網羅性 完全保証 保証なし 部分的(設定依存)
保守性 中(設定依存)

assertNeverの利点

  1. 網羅性の保証: すべてのケースが処理されていることを保証します。
  2. リファクタリングの安全性: 型の定義変更時に関連箇所の更新漏れを防ぎます。
  3. ランタイムの安全性: 想定外の値に対して明示的なエラーをスローします。

注意点

  1. assertNeverは主にコンパイル時のチェックのために使用されます。
  2. 実行時にassertNever関数が呼び出されることは、通常はバグを意味します。

まとめ

assertNeverを使用することで、TypeScriptの型システムをより活用し、コードの安全性と保守性を高めることができます。

参考リンク

Discussion