Menu

TypeScriptのユニオン型(|): 使い方とサンプル

string | number のようなユニオン型は、値がいくつかの型のどれか1つであることを表します。ユニオン型でできること(すべてのメンバーが持つものだけ)、絞り込み方、リテラルやオブジェクト型のユニオン、(A | B)[] と A[] | B[] の違いを解説します。

このページのコードはエディタで実行できます - 編集してすぐに結果を確認できます。

ユニオン型は | で選択肢を並べます。string | number 型の値は、文字列か数値のどちらかです。ユニオン型は、正当に複数の形を取りうる値をTypeScriptが表す方法で、コンパイラーは、ある形に特有のものを使う前に、どの形なのかをチェックさせます。

typeof のチェックの各ブランチの中では、id は1つの型です。このステップを絞り込みといい、ユニオン型を実用的にしているのはこれです。

使えるのは共通のメンバーだけ

絞り込む前は、ユニオン型のすべてのメンバーが対応しているものしか使えません。toString() は文字列にも数値にもあるので使えますが、toUpperCase() は文字列にしかありません:

index.ts(3,16): error TS2339: Property 'toUpperCase' does not exist on type 'string | number'.
  Property 'toUpperCase' does not exist on type 'number'.

2行目は、そのプロパティを持たないメンバーを名指ししています。逆方向にも同じルールが当てはまります。string | number の値は、数値かもしれないので、string 型の引数には渡せません(TS2345)。ユニオン型はより多くの値を受け付ける代わりに、チェックするまではできることが少なくなります。

ユニオン型の絞り込み

絞り込みには普通のJavaScriptのチェックを使います。TypeScriptは制御フローをたどり、除外されたメンバーを取り除いていくので、最後のチェックのあとには1つのメンバーだけが残ります:

チェック絞り込む先向いている対象
typeof x === "string"そのプリミティブstring、number、boolean、bigint、symbol、undefined、function
x === null、x === "a"比較した値null、undefined、リテラルのメンバー
Array.isArray(x)配列のメンバー配列
x instanceof Dateそのクラスクラスのインスタンス
"meow" in xそのプロパティを持つメンバーオブジェクト型
x.kind === "circle"そのタグを持つメンバー判別可能なユニオン型
isCat(x)(x is Cat を返す)関数が示すもの何でも、独自のロジック

絞り込みの方法の一覧は、型の絞り込みのページにあります。

リテラル型のユニオン

リテラルの値のユニオン型は、許可する値の閉じた集合です。実際のコードでいちばんよく使われるユニオン型です:

type Status = "idle" | "loading" | "success" | "error";
type Dice = 1 | 2 | 3 | 4 | 5 | 6;
type Toggle = "on" | "off" | boolean; // boolean is itself true | false

let current: Status = "idle";
current = "loading";   // fine
current = "finished";  // error TS2322: Type '"finished"' is not assignable to type 'Status'.

リテラルと比較すると絞り込まれます。if (current === "error") のあと、else のブランチでは current がほかの3つのどれかだとわかります。多くのコードベースでは、リテラルのユニオン型が enum の代わりになっています。as const と、配列からこうしたユニオン型を作る方法は リテラル型 を参照してください。

オブジェクト型のユニオン

メンバーがオブジェクト型の場合、すべてに共通するプロパティは直接使えます。それ以外は、in でプロパティがあるかをチェックします:

複数のオブジェクトの形のユニオン型なら、kind: "cat" / kind: "fish" のような共通のリテラルのプロパティを使うパターンのほうがすっきりします。その1つのプロパティをチェックすればオブジェクト全体が絞り込まれ、それに対する switch は網羅性をチェックできます。このパターンが判別可能なユニオン型です。

配列とユニオン型

かっこの位置で意味がまったく変わります:

型意味値の例
(string | number)[]各要素が文字列か数値である配列[1, "two", 3]
string[] | number[]文字列だけの配列、または数値だけの配列["a", "b"]
string | number[]文字列、または数値の配列(| は [] より結合が弱い)"text"

(string | number)[] を反復すると、各要素はユニオン型なので、上の reduce のコールバックのように絞り込みが必要です。map や filter のようなメソッドは string[] | number[] にも使え、コールバックは string | number を受け取ります。

null と undefined を含むユニオン型

いちばんよく使われるユニオン型は「値か、何もないか」です: string | null、User | undefined。Array.prototype.find と Map.prototype.get が返すのはこれで、省略可能なプロパティ name?: string を読むと string | undefined になります。?.、??、null チェックによる扱い方には専用のページがあります: null と undefined。

既存のユニオン型から型のレベルでメンバーを取り除くには、組み込みのユーティリティを使います。Exclude<"a" | "b" | "c", "a"> は "b" | "c"、NonNullable<string | null> は string です。

よくある質問

TypeScriptのユニオン型とは何ですか?

| でつないだ複数の選択肢からなる型です。string | number 型の値は、文字列か数値のどちらかです。typeof value === "string" のようなチェックで値を1つのメンバーに絞り込むまで、コンパイラーはすべてのメンバーに共通するものしか使わせません。

ユニオン型でプロパティが存在しないと言われるのはなぜですか?

ユニオン型のメンバーの少なくとも1つがそのプロパティを持っていないからです。たとえばエラー TS2339 Property 'toUpperCase' does not exist on type 'string | number' は、値が toUpperCase を持たない数値かもしれないという意味です。先に絞り込み(typeof、in、Array.isArray、instanceof、または判別プロパティのチェック)、それからそのメンバー特有のプロパティを使いましょう。

複数の型を持つ配列を宣言するには?

ユニオン型をかっこで囲みます: (string | number)[] または Array<string | number> で、各要素がどちらの型でもかまいません。string[] | number[] は違い、配列全体がすべて文字列か、すべて数値です。かっこがないと、string | number[] は文字列か数値の配列という意味になります。

ユニオン型と交差型の違いは何ですか?

ユニオン型 A | B は型のどれか1つである値なので、共通するものしか使えません。交差型 A & B は同時に両方である値なので、両方のメンバーをすべて持ちます。オブジェクト型では、A | B はより多くの値を受け付け、A & B はより多くのプロパティを要求します。

ユニオン型の値がどの型かをチェックするには?

TypeScriptが理解できる実行時のチェックを使います。プリミティブには typeof x === "string"、配列には Array.isArray(x)、クラスには x instanceof Date、オブジェクトの形には "prop" in x、メンバーが共通のリテラルのタグを持つなら x.kind === "circle" です。独自のロジックには、x is T を返す型ガード関数を書きます。

Coddy programming languages illustration

Coddyでコードを学ぼう

始める