Skip to content

6.1 ユニオン型とインターセクション型

静的型付き言語の中でもTypeScriptで特に頻繁に使われる、型同士を組み合わせる強力な機能について学習します。

ユニオン型(union type)は、T | U と書き、「型 T または型 U」を表現します。

  • 型の合成: type User = Animal | Human とすれば、Animal 型または Human 型のどちらかを満たす値を受け入れることができます。
  • プロパティアクセスの制限: ユニオン型の値に対しては、どちらの型かわからない状態では、構成要素の「すべて」に共通して存在するプロパティしかアクセスできません(コンパイルエラーになります)。

コラム26 2種類の「ない」:存在しないかもしれないプロパティを取得できない理由

Section titled “コラム26 2種類の「ない」:存在しないかもしれないプロパティを取得できない理由”

TypeScriptでは、プロパティが「ない」という状況には2種類あります。

  1. 「実際にない」: age?: number のようにオプショナルで定義された場合、型としては存在するが値が入っていない(undefined になる)状態です。
  2. 「型にない」: その型定義自体にプロパティが存在しない状態です。ユニオン型で「片方の型にしかないプロパティ」にアクセスしようとすると、構造的部分型の性質上、実際にはどうなっているか全く不明なため、TypeScriptは安全のためにアクセスを禁止(コンパイルエラー)します。

ユニオン型を持つ値に対して操作を行った場合、その結果もユニオン型になることがあります。

  • プロパティの型: 両方の型に存在するプロパティにアクセスした場合、その結果は「それぞれのプロパティの型を集めたユニオン型」になります(例: string | number)。
  • 関数の型: 関数型のユニオン型(「Aを返す関数」または「Bを返す関数」)を呼び出すと、その返り値は「AまたはB」のユニオン型になります。

6.1.3 インターセクション型とは

Section titled “6.1.3 インターセクション型とは”

インターセクション型(intersection type)は、T & U と書き、「型 T であり、かつ型 U でもある」を表現します。

  • オブジェクト型の拡張: { species: string } & { name: string } とすると、両方のプロパティを併せ持つ1つのオブジェクト型になります。
  • never型: string & number のように、同時に満たすことが不可能なプリミティブ型同士を組み合わせると、属する値が存在しない never 型となります。

6.1.4 ユニオン型とインターセクション型の表裏一体な関係

Section titled “6.1.4 ユニオン型とインターセクション型の表裏一体な関係”

関数型同士のユニオン型を呼び出そうとする場合、引数の型にはインターセクション型が要求されます。

  • 理由: どちらの関数が来ても安全に実行できるようにするためには、引数は「どちらの関数が要求する型でも受け入れられる(両方の条件を満たす)値」でなければならないからです。

コラム27 ユニオン型・インターセクション型と型の共変・反変の関係

Section titled “コラム27 ユニオン型・インターセクション型と型の共変・反変の関係”

関数型同士のユニオン型を作った場合、返り値(共変の位置)はそのままユニオン型になりますが、引数(反変の位置)はインターセクション型に変換されるという、型の理論的な裏付けが存在します。

6.1.5 オプショナルプロパティ再訪

Section titled “6.1.5 オプショナルプロパティ再訪”

「値がないかもしれない」ことを表す方法は2通りあります。

  • age?: number: プロパティ自体を省略できます(undefined も代入可)。
  • age: number | undefined: 省略はできず、明示的に undefined を設定する必要があります。
  • exactOptionalPropertyTypes オプションを有効にすると、前者の書き方で明示的に undefined を代入できなくなり、両者の役割がより明確に分かれます。

6.1.6 オプショナルチェイニングによるプロパティアクセス

Section titled “6.1.6 オプショナルチェイニングによるプロパティアクセス”

obj?.prop という構文(オプショナルチェイニング)を使うと、objnullundefined の場合でもランタイムエラーを起こさず、安全に undefined を返してくれます。

  • 関数/メソッド呼び出し: func?.()obj?.method() のように使うこともできます。
  • チェインのスキップ: obj?.foo.bar のように繋げた場合、objnull 等であれば、それ以降のアクセス(.foo.bar)はまとめてスキップされます。