TypeScriptのユニオン型(|)とは?使い方と基本構文
生徒
「先生、TypeScriptで“ユニオン型”ってよく聞くんですけど、どんなときに使うんですか?」
先生
「良い質問ですね。ユニオン型は、変数が“いくつかの型のうちどれか”になる可能性があるときに使うものです。」
生徒
「“いくつかの型”ってどういう意味ですか?」
先生
「例えば、“文字列または数値”のどちらかを入れたいときに便利なんですよ。では、基本構文から見てみましょう!」
1. ユニオン型(Union Type)とは?
TypeScriptのユニオン型(Union Type)とは、|(パイプ)を使って「複数の型のどれか1つを受け入れる」ことができる型のことです。
英語で「Union(ユニオン)」とは「結合」や「ひとまとめにする」という意味があります。
たとえば、string型(文字列)とnumber型(数値)のどちらかを許容したいとき、ユニオン型を使うと次のように書けます。
let value: string | number;
value = "Hello"; // OK(文字列)
value = 100; // OK(数値)
value = true; // エラー(booleanは許されない)
このように、|を使うことで、型の選択肢を増やすことができます。JavaScriptではどんな型でも代入できましたが、TypeScriptでは型安全性を保ちつつ、柔軟なコードを書くことができます。
2. ユニオン型を使うメリット
ユニオン型を使う最大のメリットは、「型の柔軟性」と「型チェックの安全性」を両立できることです。
特に、関数の引数で「文字列でも数値でも対応したい」といったケースで非常に便利です。
function printId(id: string | number) {
console.log("ID:", id);
}
printId("abc123"); // OK
printId(456); // OK
このように、ユニオン型を使うことで、同じ関数で異なる型を扱うことができるようになります。
一方で、TypeScriptは型チェックをしてくれるため、想定外の型(例えばboolean)を渡すとエラーで教えてくれます。
3. 型に応じた処理を分ける(型ガード)
ユニオン型を使うと、変数が「どの型なのか」を明確に判断してから処理を分ける必要があります。
このときによく使われるのが、型ガード(Type Guard)という仕組みです。
function getLength(value: string | number) {
if (typeof value === "string") {
// ここでは value は string 型として扱われる
return value.length;
} else {
// ここでは value は number 型として扱われる
return value.toString().length;
}
}
console.log(getLength("Hello")); // 5
console.log(getLength(12345)); // 5
このように、typeofを使って型を判定すると、その条件の中ではTypeScriptが自動的に型を絞り込んでくれます。これを型の絞り込み(Type Narrowing)と呼びます。
4. ユニオン型と配列の組み合わせ
ユニオン型は配列でも使うことができます。
「配列の中に文字列か数値が入る」ようなケースでは、次のように書けます。
let items: (string | number)[] = ["apple", 100, "banana", 200];
このようにすることで、配列の中身がすべて文字列または数値であることをTypeScriptが保証してくれます。
誤ってbooleanやnullを入れた場合はエラーになります。
5. ユニオン型とリテラル型を組み合わせる
ユニオン型は、リテラル型(Literal Type)と組み合わせることで、より厳密な値の制限もできます。
リテラル型とは、「特定の値だけを許す型」です。
let direction: "up" | "down" | "left" | "right";
direction = "up"; // OK
direction = "left"; // OK
direction = "top"; // エラー!定義されていない値
このように書くと、「up・down・left・right」以外の文字列を代入するとエラーになります。
ゲーム開発やメニュー操作などで、取りうる値を限定したい場合にとても便利です。
6. ユニオン型の注意点
ユニオン型を使うときに注意すべきなのは、「共通して使えるプロパティやメソッドしか直接使えない」という点です。
例えば、string | number型では、どちらの型にも共通しないメソッド(例えばtoFixed()など)を使おうとするとエラーになります。
let data: string | number = 42;
// data.toFixed(2); // エラー:number型にしか存在しないメソッド
このような場合は、先ほど紹介した型ガードを使って、typeofで型を判定してから安全に呼び出すようにしましょう。
7. 現実的なユースケース例
実際の開発では、APIレスポンスやフォーム入力などで「複数の型が入り得る」ケースがよくあります。例えば、IDが文字列のときもあれば数値のときもあるとき、ユニオン型が活躍します。
type UserId = string | number;
function showUserId(id: UserId) {
console.log(`ユーザーID: ${id}`);
}
showUserId("abc123");
showUserId(987);
このように、ユニオン型は型の柔軟さを保ちつつ、エラーを防ぐための安全なコードを書く手助けをしてくれます。
まとめ
ユニオン型の基本を振り返る
この記事では、TypeScriptにおけるユニオン型の基本構文から実践的な使い方までを段階的に学んできました。ユニオン型とは、複数の型のうち「どれか一つ」を受け入れることができる型定義の方法であり、|(パイプ)を使って表現されます。JavaScriptでは暗黙的にさまざまな型を扱えていましたが、TypeScriptでは型安全性を保ちながら柔軟な設計を行う必要があります。その両立を可能にする仕組みが、ユニオン型の大きな特徴です。
文字列または数値、特定の文字列だけを許可するリテラル型との組み合わせ、配列との併用など、ユニオン型は多くの場面で活躍します。特に関数の引数や戻り値、外部から受け取るデータの型定義において、ユニオン型を使うことで現実的なデータ構造を正確に表現できるようになります。
型ガードと型の絞り込みの重要性
ユニオン型を使う際に欠かせない考え方が、型ガードと型の絞り込みです。変数が複数の型を取りうる場合、そのままでは使えないプロパティやメソッドが存在します。そのため、typeofなどを使って実行時に型を判定し、処理を分岐させる必要があります。
この型の絞り込みによって、TypeScriptは「この条件の中では文字列型」「こちらの条件では数値型」と正確に理解してくれます。結果として、エラーを未然に防ぎつつ、可読性の高いコードを書くことができます。ユニオン型は自由度が高い分、型を意識した設計が重要になる点も理解しておきましょう。
ユニオン型を使った実践的なサンプル
function formatValue(value: string | number) {
if (typeof value === "string") {
return "文字列:" + value;
} else {
return "数値:" + value.toString();
}
}
console.log(formatValue("テスト"));
console.log(formatValue(123));
このサンプルでは、ユニオン型と型ガードを組み合わせることで、安全に処理を分岐しています。実務では、フォーム入力値やAPIレスポンスなど、型が一意に定まらない場面が多くあります。そのようなケースでも、ユニオン型を正しく使えば、TypeScriptの型チェックの恩恵を最大限に受けることができます。
生徒
「ユニオン型って、最初はただ型を増やしているだけだと思っていましたけど、実際には現実のデータ構造を表現するために必要なんですね。」
先生
「その通りです。現実のプログラムでは、必ずしも一つの型だけで表せない場面が多いですよね。ユニオン型は、そうした状況を無理なく表現するための仕組みです。」
生徒
「型ガードを使わないとエラーになる理由も分かってきました。TypeScriptが安全に扱えるように、ちゃんと型を判定してあげる必要があるんですね。」
先生
「はい。型ガードはユニオン型とセットで覚えると理解が深まります。型を意識して書くことで、バグの少ないコードになりますよ。」
生徒
「これからは、stringかnumberか迷うような変数が出てきたら、ユニオン型を使うことを考えてみます。」
先生
「それは良いですね。TypeScriptのユニオン型を使いこなせるようになると、設計の幅が大きく広がります。ぜひ実際の開発でも積極的に使ってみてください。」