zukucode
主にWEB関連の情報を技術メモとして発信しています。

TypeScript Union型を条件分岐で安全に使い分ける

APIの結果が成功または失敗のどちらかになる場合、両方の項目を任意にした1つの型へ詰め込むと、存在しない値を読みやすくなります。Union型なら、あり得る状態を別々に表せます。

状態ごとに型を定義する

type SaveResult =
  | { status: 'success'; id: string }
  | { status: 'error'; message: string };

statusはどの型かを見分ける目印です。成功時にmessage、失敗時にidはありません。

statusで分岐する

const showResult = (result: SaveResult): string => {
  if (result.status === 'success') {
    return `保存しました: ${result.id}`;
  }
  return result.message;
};

分岐後はTypeScriptが型を絞り込むため、asによる型アサーションは不要です。

これを「成功か失敗か」を示す1つの型として id?: stringmessage?: string にすると、成功時でも id がない状態を型が許してしまいます。状態と必要な値を組にできる点がUnion型の利点です。

新しい状態を増やす

読み込み中も表したければ、{ status: 'loading' }をUnionへ追加します。各状態に必要な項目だけを定義でき、不要なundefinedチェックを減らせます。

状態が増えても分岐の書き忘れを検出したい場合は、switchneverを使えます。

const describe = (result: SaveResult): string => {
  switch (result.status) {
    case 'success': return `ID: ${result.id}`;
    case 'error': return result.message;
    default: {
      const unhandled: never = result;
      return unhandled;
    }
  }
};

SaveResultへ新しい状態を追加すると、defaultの代入が型エラーになり、表示方法の追加を促します。

Union型はコンパイル時の約束です。APIから届く値が本当にその形かは、必要に応じて実行時に検証してください。


関連記事