【入門編】Type Aliasで実現する「判別可能な共用体(Discriminated Unions)」の高度なモデリング – TypeScript コア・型システムの基礎解析バイブル

こんにちは。TypeScriptの深淵へようこそ。

日々コードを書いていると、「この変数、今はどういう状態なんだっけ?」と迷うことはありませんか? APIから返ってきたレスポンス、UIの読み込み中・成功・エラー……。これらを単純なオブジェクトで管理すると、すぐに型安全性の迷宮に迷い込んでしまいます。

今日は、TypeScriptを「最強の武器」に変えるための登竜門、「判別可能な共用体(Discriminated Unions)」をマスターしましょう。これさえ理解すれば、あなたの書くコードの堅牢性は、明日から劇的に変わります。

—

1. なぜ「判別可能な共用体」が必要なのか?

まず、陥りがちな「アンチパターン」を見てみましょう。

type State = {
status: ‘loading’ | ‘success’ | ‘error’;
data?: string; // 成功した時だけ欲しい
error?: Error; // エラーの時だけ欲しい
};

この型には致命的な欠陥があります。`status` が `’success’` なのに `error` プロパティが存在していても、型システムは文句を言いません。「あり得ない状態」を型で表現できていないのです。これでは、使うたびに `if (state.data)` のようなチェックを繰り返す羽目になります。

そこで登場するのが「判別可能な共用体」です。

—

2. 判別可能な共用体の実装:状態を「型」で定義する

判別可能な共用体とは、「共通のプロパティ(判別子)」を持つ複数の型を `|` (Union) で繋いだものです。

type LoadingState = { kind: ‘loading’ };
type SuccessState = { kind: ‘success’; data: string };
type ErrorState = { kind: ‘error’; error: Error };

// これが「判別可能な共用体」の完成形です
type State = LoadingState | SuccessState | ErrorState;

ここでの `kind` が「判別子(Discriminant)」です。この魔法のプロパティのおかげで、TypeScriptは `kind` の値を見るだけで、そのオブジェクトがどの型であるかを100%特定できるようになります。

—

3. switch文による「網羅性チェック(Exhaustive Check)」

TypeScriptの真骨頂は、この状態を `switch` 文で扱う時に発揮されます。

function handleState(state: State) {
switch (state.kind) {
case ‘loading’:
console.log(‘読み込み中…’);
break;
case ‘success’:
// ここでは state は SuccessState であると確約されている!
console.log(‘データ:’, state.data);
break;
case ‘error’:
console.error(‘エラー:’, state.error.message);
break;
default:
// ここで「網羅性チェック」を行います
const _exhaustiveCheck: never = state;
return _exhaustiveCheck;
}
}

なぜ `never` を使うのか?

もし将来、`type State` に `type IdleState = { kind: ‘idle’ }` を追加したとします。すると、`switch` 文で `case ‘idle’` を書き忘れた瞬間、TypeScriptコンパイラが「`IdleState` 型を `never` に割り当てることはできません」とコンパイルエラーを出して教えてくれます。

つまり、「新しい状態を追加したのに、処理を書き忘れる」というバグを物理的に排除できるのです。これがTypeScriptの魂です。

—

4. 陥りやすい罠:型エイリアスとインターフェースの違い

初心者がよく悩むのが「`type` と `interface` どっちがいいの?」という問題です。

  • `type` (型エイリアス): 共用体(Union)や交差型(Intersection)など、複雑な型を組み立てるのに適しています。
  • `interface`: オブジェクトの形状を定義し、拡張(extends)するのに適しています。

今回の「判別可能な共用体」のように、状態を分岐させる場合は `type` を使うのが一般的です。`interface` は複数の型を `|` で繋ぐことができないため、このパターンには `type` が最も適しているのです。

—

5. まとめ:型を「文書」ではなく「制約」にする

判別可能な共用体を使うということは、単に型を書くことではありません。「プログラムの状態遷移を、コンパイラが監視可能なルールとして書き下す」ということです。

1. kind プロパティを共通化する。
2. Union 型として定義する。
3. switch 文で網羅的に分岐させ、`never` で漏れをガードする。

この3ステップを意識するだけで、あなたの書くコードは「動く」だけでなく「壊れない」ものへと進化します。

「ここをクリアすれば、TypeScriptの基本はバッチリマスターできますよ」。さあ、次はどんな複雑な状態を型で美しく整理しましょうか? 次のコードを書き始める時、ぜひこの「判別可能な共用体」を思い出してくださいね。

タイトルとURLをコピーしました