logo

分岐漏れをコンパイル時につぶす

2026-03-04
a month ago

開発環境

  • typescript 5.9.3

前提

TypeScript を使っていると、型安全性を高めたい場面によく遭遇します。

特に 列挙型の分岐 (switch / if) では、ケースの抜け漏れがあると意図しない挙動につながります。

たとえば Enum を扱うコードで

enum Direction {
  Up,
  Down,
  Left,
  Right,
}

const move = (dir: Direction) => {
  switch (dir) {
    case Direction.Up:
      return "↑";
    case Direction.Down:
      return "↓";
    // Left, Right を処理し忘れてしまった...
  }
};

コンパイルエラーにはならず、一部のケースが未対応のままコードが通ってしまいます。

これでは 分岐漏れ を検知できません。

この問題を コンパイル時に検知したい というニーズが出てきます。

本題

分岐漏れチェックの基本パターン

TypeScript で分岐漏れを確実に検出する代表的な方法は、未到達コードの型を never にすることです。

const assertNever = (x: never): never => {
  throw new Error("Unexpected value: " + x);
};

const move = (dir: Direction) => {
  switch (dir) {
    case Direction.Up:
      return "↑";
    case Direction.Down:
      return "↓";
    case Direction.Left:
      return "←";
    case Direction.Right:
      return "→";
    default:
      return assertNever(dir); // ここで漏れをチェック
  }
};

dir が未処理のケースを含んでいるとコンパイルエラーになります。

ただしこの方法は、毎回 assertNever を用意する必要があり、やや冗長に感じることもあります。

satisfies never を使ったよりシンプルな方法

TypeScript 4.9 以降で使える satisfies を活用すると、より直感的に「ここには到達しないはず」ということを表現できます。

const move = (dir: Direction) => {
  switch (dir) {
    case Direction.Up:
      return '↑';
    case Direction.Down:
      return '↓';
    case Direction.Left:
      return '←';
    case Direction.Right:
      return '→';
    default:
      return dir satisfies never; // ここで漏れをチェック
  }
};

オブジェクト網羅チェックにも応用できる

switch だけでなく、Direction のマッピングをオブジェクトで表現する場合も同様です。

const directionLabel = {
  [Direction.Up]: "↑",
  [Direction.Down]: "↓",
  [Direction.Left]: "←",
  [Direction.Right]: "→",
} satisfies Record<Direction, string>;

いずれかを書き忘れると、その場で型エラーになります。

as Record<...> と違い、

satisfies Record<...> は 型を壊さずに検証だけを行う のがポイントです。

なぜ satisfies never が良いのか

  • 追加の関数を用意する必要がない
  • 「ここは到達不能であるべき」という意図が明確
  • 型アサーション (as) と違って安全性を壊さない
  • enum / union が増えた瞬間に漏れが検知できる

分岐漏れはレビューでは見落としやすく、実行時バグにもつながります。

それを コンパイル時に確実に潰せる のは TypeScript の強みです。

さいごに

TypeScript を使う最大の価値は、「実行前にバグを消せること」です。

never を活用した exhaustive check は以前からあるテクニックですが、 satisfies never を使うことで、よりシンプルに・意図が伝わる形で書けるようになりました。

型システムに仕事をさせる設計を意識すると、コードは確実に強くなります。 分岐が増えがちなプロジェクトほど、ぜひ取り入れてみてください。

参照