TypeScript | 基礎文法:Union・基本型操作 – union型の基本

TypeScript
スポンサーリンク

union型は「AかBかどちらか」の型

まずイメージからいきます。
union型(ユニオン型)は、「この値は“これか、あれか”のどれかです」という“選択肢のある型」です。

書き方はとてもシンプルで、型と型のあいだを | でつなぎます。

let value: string | number;

value = "hello"; // OK
value = 123;     // OK
value = true;    // エラー(string でも number でもない)
TypeScript

この例だと、value は「stringnumber のどちらか」でなければいけません。
boolean を代入しようとすると、TypeScript が「その選択肢はないよ」と止めてくれます。

ここで重要なのは、「union型は“何でもOK”ではなく、“決められた候補の中からだけOK”という厳しい型」ということです。


union型の基本文法と読み方

型を「AまたはB」として宣言する

最も基本的な書き方は、次のような形です。

let id: string | number;

id = "user-1"; // OK
id = 42;       // OK
TypeScript

「この変数 id には、文字列か数値のどちらかが入る」という意味になります。

関数の引数にもよく使います。

function printId(id: string | number) {
  console.log("ID:", id);
}

printId("user-1"); // OK
printId(42);       // OK
TypeScript

呼び出し側から見ると、「string でも number でも好きな方を渡していいよ」という“柔軟さ”を持った関数になります。

ただし、中で扱うときは注意が必要です。ここが次のポイントです。


union型の重要ポイント:「共通して保証されているもの」しか使えない

どちらの型にも存在するメソッドしか直接呼べない

string | number のような union型の値に対しては、両方の型に共通して存在するものしか、直接は使えません。

function printId(id: string | number) {
  // console.log(id.toUpperCase()); // エラー
}
TypeScript

TypeScript が怒る理由はこうです。

id は string かもしれないし、number かもしれない。
string なら toUpperCase があるけど、number にはない。
だから“今ここで toUpperCase を呼んでいい”とは言えない。」

つまり、union型の値をそのまま使うときは、「両方の型に共通してあるプロパティ・メソッドだけOK」というルールになります。

function printId(id: string | number) {
  console.log(id.toString());  // OK(string にも number にも toString はある)
}
TypeScript

toString はどちらの型にもあるので、これはコンパイルが通ります。

ここがすごく大事なポイントで、
「ユニオン型は“幅が広いぶん、直接できることは制限される”」と理解してください。


union型と条件分岐(型ガード)をセットで考える

typeof で型を判定してから、その型として扱う

union型を「ちゃんと使える形」にするには、条件分岐で型を絞り込む(型ガード)というステップがほぼ必須です。

function printFormattedId(id: string | number) {
  if (typeof id === "string") {
    console.log("文字列ID:", id.toUpperCase());
  } else {
    console.log("数値ID:", id.toFixed(2));
  }
}
TypeScript

ここでは、id の型は最初 string | number ですが、

if (typeof id === "string") の中
idstring に絞り込まれる

else の中
→ 残りの number に絞り込まれる

というふうに、TypeScript がブロックごとに型を狭めてくれます。

この「union型+型ガード」の組み合わせが理解できると、TypeScript での型設計が急に楽しくなります。


リテラルunion型で「取りうる値」を限定する

“ON” | “OFF” のような「モード」を型にする

union型は、単に string | number のような広い型だけでなく、「特定の文字列・数値のみに絞った型」を作るのにも非常に強力です。

type SwitchState = "ON" | "OFF";

let state: SwitchState;

state = "ON";   // OK
state = "OFF";  // OK
state = "Other"; // エラー(SwitchState にその選択肢はない)
TypeScript

SwitchState は、「文字列なら何でもいい」わけではありません。
“ON” か “OFF” のどちらかだけ、という“厳密な選択肢”になっています。

関数でも同じです。

function setState(state: "ON" | "OFF") {
  console.log("状態:", state);
}

setState("ON");    // OK
setState("OFF");   // OK
setState("OTHER"); // エラー
TypeScript

ここで重要なのは、「自由度を下げることで、間違いを防いでいる」という設計の感覚です。

適当なスペルの “Onn” や “off” をうっかり書くと、その場でエラーになります。
「ありうる状態」を型で表現してしまうことで、バグの入り込む余地を減らせるわけです。

オブジェクトと組み合わせると「状態のバリエーション」を型にできる

もう一歩進むと、こうなります。

type LoadingState = 
  | { status: "idle" }
  | { status: "loading" }
  | { status: "success"; data: string }
  | { status: "error"; error: string };
TypeScript

これは、「読み込み処理の状態」を4パターンの union型で表現している例です。

status が “success” のときだけ data を持ち、
“error” のときだけ error を持つように設計されています。

この型を使うと、関数の中で安全に分岐できます。

function render(state: LoadingState) {
  if (state.status === "idle") {
    console.log("何もしていません");
  } else if (state.status === "loading") {
    console.log("読み込み中…");
  } else if (state.status === "success") {
    console.log("成功:", state.data);
  } else {
    console.log("失敗:", state.error);
  }
}
TypeScript

state.status === "success" のブロックでは、
state{ status: "success"; data: string } 型に絞り込まれるので、state.data を安全に使えます。

ここまで来ると、

union型=「アプリの状態のバリエーションを、漏れなく・被りなく列挙したもの」

として使えるようになります。
これは TypeScript の大きな魅力です。


union型を設計するときに考えるべきこと

「本当に“どちらか”でいいのか?」を自分に問いかける

union型を使うときに一番大事なのは、「ここは“どんな値でも来ていい”場所ではなく、“この候補のどれかだけ”来ていい場所なんだ」と意識することです。

例えば次のような型があったとします。

type Id = string | number;
TypeScript

このとき、自分に問いかけてみてください。

なぜ ID は string と number の両方で表現されうるのか?
片方に統一できない理由は何か?
呼び出し側は「どっちで渡すべきか」迷わないか?

もし、「実は全部 string にした方がシンプル」という結論なら、union にせず string に統一した方が設計としては素直です。

逆に、「サーバーからは数値、フロントでは文字列として扱うことがある」などの理由が明確にあるなら string | number も妥当です。

union型を使うときは、「どんなケースを許し、どんなケースを許さないか」の線引きを自覚的に決める
これが、型設計の質を上げる一番のポイントです。


まとめ:union型の“本質”を一言で言うと

union型は、ただの「型を並べる記号」ではありません。

「この値は、AでもいいしBでもいい。でも、それ“以外”は絶対に許さない」
という、「許される選択肢のリスト」そのものです。

だからこそ、

柔軟に受け取るべきところでは string | number のような union を使い
状態やモードを厳密にしたいところでは "ON" | "OFF" のようなリテラル union を使い
条件分岐や型ガードと組み合わせて、「どの世界ではどんなプロパティが使えるか」をはっきりさせる

という形で、「現実世界の“場合分け”をそのまま型に落とし込む道具」として活かしてほしいです。

コードを書いていて、

「ここは A のときもあれば B のときもあるな」
と思ったら、そこが union型を検討するタイミングです。
その「場合分け」を逃げずに型にしていくことが、TypeScript の一番おいしい部分につながっていきます。

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