TypeScriptのユニオン(Union)型とは?型ガードで「どの型か」を安全に絞り込む方法
TypeScriptで型の勉強を進めていると、|(パイプ)で型をつないだ書き方を見かけることがありますよね。
string | number みたいな書き方をよく見るけど、これって何?ユニオン型とは何なのか全然わかってない
この記事では、ユニオン型の基本的な書き方から、型ガードを使って「今どの型か」をTypeScriptに伝える方法まで順番に解説します。「ユニオン型を書いたらエラーが出た」「型ガードの使い方がわからない」という状況をスッキリ整理することを目標にしています。
- TypeScriptのユニオン型(
string | numberのような書き方)の意味がよくわからない方 - ユニオン型を使ったらエラーが出て困っている方
- 型ガードって何?というところから整理したい方
- typeof・in・instanceof の違いと使い分けを理解したい方
この記事を読み終えると、ユニオン型の基本的な書き方がわかり、型ガードを使って型を安全に絞り込む方法が理解できるようになります。
それでは、順を追って詳しく見ていきましょう!
- 未経験で後悔したくない
【実体験】未経験からITエンジニアに転職して後悔した話|4社経験してわかった「最初の選択ミス」 - 年収が低くて不安
4年間ずっと年収260万だったエンジニアが、転職で510万になるまでの全記録
ユニオン型とは — 「どれかの型」を表現する仕組み
ユニオン型は、「この変数はAかBのどちらかの型だよ」と伝えるための書き方です。
TypeScriptでは変数や引数に「この型の値しか入れられない」というルールを設けられます。でも実際のプログラムでは、「文字列が来ることもあれば、数値が来ることもある」という場面がよくあります。そういうときに使うのがユニオン型です。
基本的な書き方(| で型をつなぐ)
ユニオン型は |(パイプ)で型を並べて書きます。読み方は「または」だと思ってもらうとイメージしやすいです。
let value: string | number;
value = "hello"; // OK:文字列を代入できる
value = 42; // OK:数値を代入できる
value = true; // エラー:boolean はユニオンに含まれていない
string | number は「文字列か数値のどちらか」という意味で、true のような boolean は含まれていないのでエラーになります。TypeScriptがしっかり教えてくれます。
型は3つ以上並べることもできます。
let status: "loading" | "success" | "error";
// この3つの文字列のどれかしか入れられない
こういった「特定の文字列のどれか」という書き方は実際のコードでもよく登場するので、見かけたらユニオン型だと思ってください。
変数と関数の引数でユニオン型を使う
ユニオン型は関数の引数にも使えます。
function printId(id: string | number): void {
console.log("ID:", id);
}
printId("user-001"); // OK
printId(42); // OK
ユニオン型を使うとどんな問題が起きるか
ユニオン型を使い始めると、ほぼ確実にこんな壁にぶつかります。
TypeScriptが「どの型かわからない」と言ってくる
「文字列なら大文字に変換したい」という処理を書いてみましょう。
function greet(name: string | number): void {
console.log(name.toUpperCase()); // エラー!
}
これはエラーになります。TypeScriptから見ると、name は「文字列か数値かわからない状態」です。数値に toUpperCase() は存在しないので、TypeScriptが「それは安全じゃないよ」と教えてくれているわけです。
これが型ガードが必要になる理由
ユニオン型の変数に対して型固有のメソッドを使うには、「今この瞬間、この値はどの型か」をTypeScriptに伝える必要があります。
それをやるのが「型ガード」です。型ガードを書くことで、TypeScriptはその条件のブロックの中で「この変数の型はこれだ」とわかってくれるようになります。
型ガードとは — TypeScriptに「今この型だ」と教える方法
型ガードとは、条件分岐の中でTypeScriptに「この変数の型を絞り込む」ことを伝えるコードのことです。
「もしこの変数が文字列だったら〜」という条件を書くと、TypeScriptもその条件ブロックの中では「この変数は文字列だ」と理解してくれます。これを活用するのが型ガードの基本的な考え方です。
型ガードには主に3つあります。
typeof:プリミティブ型(文字列・数値・真偽値など)を判定するin:オブジェクトが特定のプロパティを持つかどうかで判定するinstanceof:クラスのインスタンスかどうかで判定する
それぞれ順番に見ていきましょう。
typeof で型を絞り込む
typeof の基本的な使い方
typeof は「この値の型名は何か」を文字列で返す演算子です。JavaScriptにもある機能で、TypeScriptでも同じように使えます。
function greet(name: string | number): void {
if (typeof name === "string") {
// この中では name が string として扱われる
console.log(name.toUpperCase()); // エラーにならない
} else {
// この中では name が number として扱われる
console.log(name.toFixed(2));
}
}
if (typeof name === "string") という条件を書くだけで、その if ブロックの内側では TypeScript が「name は string だ」とわかってくれます。
ポイントは「if の内側で自動的に型が絞り込まれる」という動きです。エラーが消えるだけでなく、文字列専用のメソッドも補完が効くようになります。
typeof が使える型と使えない型
typeof で判定できるのは、主に次のプリミティブ型です。
| 書き方 | 判定できる型 |
|---|---|
typeof x === "string" |
文字列 |
typeof x === "number" |
数値 |
typeof x === "boolean" |
真偽値 |
typeof x === "undefined" |
undefined |
typeof x === "function" |
関数 |
注意点として、typeof でオブジェクト型を細かく判定することはできません。 typeof でオブジェクトを調べると、種類にかかわらず "object" が返ってきます。
null も "object" になってしまうので、オブジェクト同士を区別したいときは in や instanceof を使います。
in演算子で型を絞り込む
オブジェクトのプロパティで型を判定する
in 演算子は「このオブジェクトに、特定のプロパティが存在するか」を確認します。オブジェクト型のユニオン型を絞り込むときに使います。
たとえば、「犬か猫のどちらか」というユニオン型を考えてみましょう。
type Dog = {
name: string;
bark: () => void;
};
type Cat = {
name: string;
meow: () => void;
};
function makeSound(animal: Dog | Cat): void {
if ("bark" in animal) {
// bark プロパティがあるなら Dog
animal.bark();
} else {
// bark プロパティがないなら Cat
animal.meow();
}
}
"bark" in animal は「animal オブジェクトに bark というプロパティが存在するか」という意味です。これが true なら Dog 型だとわかるので、TypeScriptもその中では Dog として扱ってくれます。
typeof ではオブジェクトの種類を区別できないので、オブジェクト同士のユニオン型には in を使うのが基本の考え方です。
ただし in を使う前提として、ユニオン内の各型で「そのプロパティの有無が異なる」必要があります。上の例なら Dog には bark、Cat には meow があるので判定できます。
もし全ての型に同じプロパティしかない場合は、in では区別できません。そういうケースは、後述のユーザー定義型ガードや Discriminated Union(判別可能なユニオン型)が役立ちます。
instanceof で型を絞り込む
クラスのインスタンスかどうかを判定する
instanceof は「このオブジェクトが、特定のクラスから作られたインスタンスかどうか」を判定します。クラスを使って型を定義している場合に使います。
class Dog {
bark(): void {
console.log("ワン!");
}
}
class Cat {
meow(): void {
console.log("ニャー!");
}
}
function makeSound(animal: Dog | Cat): void {
if (animal instanceof Dog) {
// animal が Dog クラスのインスタンスなら
animal.bark();
} else {
animal.meow();
}
}
animal instanceof Dog は「animal が Dog クラスから作られたオブジェクトか」という意味です。
instanceof が使えるのはクラスで定義した型に限られます。 type や interface で定義したオブジェクト型には使えないので、そのときは in を使いましょう。
typeof・in・instanceof の使い分けまとめ
3つの型ガードをどう使い分けるかを整理しておきます。
| 型ガード | 使う場面 | 例 |
|---|---|---|
typeof |
文字列・数値・真偽値などのプリミティブ型を判定したい | typeof x === "string" |
in |
オブジェクト同士をプロパティの有無で区別したい | "bark" in animal |
instanceof |
クラスのインスタンスかどうかを判定したい | x instanceof Dog |
迷ったときの判断基準はシンプルで、「文字列・数値・真偽値なら typeof、オブジェクトの種類を区別したいなら in、クラスを使っているなら instanceof」と覚えておくと、たいていの場面で対応できます。
ユーザー定義型ガードとは
ここまでの3つで多くの場面は対応できます。ただ、「もっと複雑な条件で型を判定したい」「同じ判定ロジックを複数箇所で使い回したい」というときのために、「ユーザー定義型ガード」という書き方があります。
is構文の基本的な書き方
ユーザー定義型ガードは、「この関数が true を返したとき、引数はこの型だ」とTypeScriptに伝える関数です。戻り値の型として 引数名 is 型名 という書き方をします。
type Dog = { name: string; bark: () => void };
type Cat = { name: string; meow: () => void };
// isDog が true を返したとき、animal は Dog 型だとTypeScriptに伝える
function isDog(animal: Dog | Cat): animal is Dog {
return "bark" in animal;
}
function makeSound(animal: Dog | Cat): void {
if (isDog(animal)) {
animal.bark(); // ここでは animal が Dog として認識される
} else {
animal.meow();
}
}
animal is Dog という部分が「is構文」です。この関数を使った if ブロックの中でも、TypeScriptが自動的に型を絞り込んでくれます。
ユーザー定義型ガードをいつ使うか
判定ロジックが複雑になってきたり、同じ型ガードを複数の場所で再利用したいときに便利です。
ただし、in や instanceof で対応できる場面ではわざわざ書く必要はありません。まずは typeof・in・instanceof の3つを使いこなせるようになることを優先して、ユーザー定義型ガードは「もっと複雑な判定がしたい」と感じたタイミングで覚えれば十分です。
【付録】さらに学びを深めるためのリソース
さらにTypescriptの学習を進めたい方のために、いくつかのリソースを紹介します。
これらのリソースを活用することで、TypeScriptの型システムについてより深い知識を得ることができるでしょう。
おすすめの書籍
ゼロからわかる TypeScript入門
技術評論社から出版されている「ゼロからわかる TypeScript入門」は、プログラミング初心者や本職プログラマーではない方を主な対象にした入門書です。
変数・条件分岐・ループといった基本から、クラスやインターフェース、モジュールまで段階的に学べる構成になっています。最終章ではWeb APIとJSONを使った非同期Webアプリの作成も体験できるので、「実際に動くものを作る」ところまで到達できます。
プロを目指す人のためのTypeScript入門
技術評論社の「プロを目指す人のためのTypeScript入門 安全なコードの書き方から高度な型の使い方まで」、通称 ブルーベリー本 です。
JavaScriptの仕様とTypeScript独自の機能を両方押さえつつ、リテラル型・ユニオン型・keyof型・ジェネリクスなど、高度な型表現まで踏み込んで解説しています。TypeScriptの型システムの表現力を本格的に学べる一冊です。
オンラインで参照できる公式ドキュメント
TypeScript公式ハンドブック
https://www.typescriptlang.org/docs/
TypeScriptの公式ドキュメントです。
intersection型を含む、すべての型システムの機能について詳細な説明があります。
TypeScript Deep Dive
https://basarat.gitbook.io/typescript/
TypeScriptの深い部分まで掘り下げて解説しているオンラインブックです。
無料で読むことができ、intersection型についても詳しく説明されています。
TypeScriptの学習は終わりがありません。
新しい機能が常に追加され、より良い書き方が発見されています。
継続的に学習を続けることで、より良いTypeScriptプログラマーになれるはずです。
まとめ — ユニオン型と型ガードをセットで覚えよう
この記事で学んだことを整理します。
ユニオン型は | を使って「AかBのどちらか」という型を表現します。
let value: string | number; // 文字列か数値のどちらか
型ガードはユニオン型の変数に対して、「今この型だ」とTypeScriptに教えるための仕組みです。
typeof:プリミティブ型の判定に使うin:オブジェクトのプロパティで型を区別したいときに使うinstanceof:クラスのインスタンスかどうかを判定するときに使う
ユニオン型と型ガードはセットで使うものです。「ユニオン型を使ったら型ガードが必要になる場面が出てくる」と頭に入れておくだけで、エラーに戸惑うことはかなり減ります。
※本記事の本文案はAIを活用して作成していますが、記載している内容およびコードは筆者が実際に調査、検証・実行し、内容の正確性を確認した上で公開しています。




