判別共用体
共通の判別子(discriminator)プロパティを持つ型の合併。TypeScriptではtypeプロパティによる絞り込みが一般的。代数的データ型の一種。
基本形
type Result<T, E> =
| { success: true; value: T }
| { success: false; error: E };
function handle(result: Result<User, Error>) {
if (result.success) {
// result.value は T 型に絞り込まれる
} else {
// result.error は E 型に絞り込まれる
}
}
Make Illegal States Unrepresentable
Make Illegal States Unrepresentableの実現手段として有効:
// 弱い構造 — 不正状態が表現可能
type Order = {
status: string;
paidAt: Date | null;
shippedAt: Date | null;
};
// 強い構造 — 不正状態がコンパイル時に排除
type Order =
| { status: "draft" }
| { status: "paid"; paidAt: Date }
| { status: "shipped"; paidAt: Date; shippedAt: Date };
判別子の二面性
判別子としてのtype属性は、文脈によって異なる性質を持つ:
| 性質 | 説明 | 結合の種類 |
|---|---|---|
| 命令的フラグ | 処理分岐を指示 | 制御結合(悪い) |
| 識別子 | データの属性を宣言 | データ結合(良い) |
例:命令的フラグ(悪い)
// バックエンドが実装詳細を操作している
{ type: "use_legacy_algorithm", data: ... }
例:識別子(良い)
// データの種類を宣言しているだけ
{ type: "user_created", userId: "123" }
TypeScriptの構造的型付けの問題
TypeScriptは構造的型付けのため、制御フラグと識別子の区別が型レベルで曖昧になりやすい。
解決策:
- Branded Type:型に名義的な区別を付与
- Symbol:ランタイムで一意な識別子を使用
境界での使い方
腐敗防止層で判別共用体を使い、入り口で一度だけ分岐を吸収するのが良いパターン。
関連
- Make Illegal States Unrepresentable - 設計原則
- 代数的データ型 - 理論的背景
- Result型 - 典型的な使用例
- 制御結合 - 濫用時の問題
- 腐敗防止層 - 境界での使い方