TypeScript のユニオン型とインターセクション型について
はじめに
ユニオン型(|
)とインターセクション型(&
)の違いを簡単な例でおさらいしておこうと思います
以下の型定義を利用して、実装します
type A = { a: string } type B = { b: number } type U = A | B type I = A & B
すべてのプロパティを実装する
const u: U = { a: '', b: 0 } const i: I = { a: '', b: 0 }
上記は当然ですが、u
とi
のどちらも TS エラーは起きません
存在しないプロパティを実装する
c
というA
とB
のどちらにも存在しないプロパティを設定する
const u: U = { a: '', b: 0, c: false } const i: I = { a: '', b: 0, c: false }
上記はu
とi
どちらもエラーになる
(そりゃそうだ)
ユニオン型で一部のプロパティを実装する
const u: U = { a: '' }
上記はエラーにならない
ユニオン型はA
とB
のプロパティのいずれかを実装していれば良い
よって、A
のプロパティ(a
)を実装しているので問題ない
const u: U = {}
上記はエラー
A
とB
のプロパティのいずれかを実装できていない
インターセクション型で一部のプロパティを実装する
const i: I = { a: '' }
上記はエラーになる
インターセクション型はA
とB
のプロパティのすべてを実装する必要がある
よって、B
のプロパティ(b
)を実装する必要がある
const i: I = {}
上記は当然エラー
A
のプロパティをオプショナル型にしてみる
プロパティをオプショナル型にするとどうなるか
type A = { a?: string } type B = { b: number } type U = A | B type I = A & B
const u: U = {}
上記はエラーにならない
ユニオン型はA
とB
のプロパティのいずれかを実装していれば良いので、上記はa
の undefined が実装できているということになる
const i: I = {}
上記はエラー
インターセクション型の場合は、A
とB
のプロパティのすべてを実装する必要があるので、この場合はb
を必ず実装する必要がある
ちなみにb
もオプショナル型にすると、上記はエラーになりません
ここまでのまとめ
ここまでの例をまとめると、ユニオン型がA
とB
のプロパティのいずれかを実装で、インターセクション型がA
とB
のプロパティのすべてを実装と言えます
今度は以下の型定義を例に実装します
type A = { a: string b: number } type B = { b: number c: string } type U = A | B type I = A & B const u: U const i: I
上記のu
とi
の変数は初期値が設定されていないため、コンパイルエラーになっています
初期値は何を与えればよいでしょうか
u
は{ a: "a", b: 1 }
もしくは、{ b: 1, c: "c" }
です(値は適当)
i
は{ a: "a", b: 1, c: "c"}
です(値は適当)
つまり、
u
はA
とB
のプロパティのいずれかを実装(b
は必須で実装し、a
とc
はいずれかが実装されていないといけない)
i
はA
とB
のプロパティのすべてを実装
となります
また、u
とi
内のプロパティを参照する際も、
u
はb
は参照できるが、a
とc
は存在するか確定させるまで参照不可となります
つまり、型ガード(a in u
)などを使って、a
やc
が存在することを確定させないと、参照できません
i
はa
,b
,c
のすべてのプロパティが参照できます
もし、上記の初期値を設定した上で、以下のようにbの型を異なるものに変えるとどうなるでしょうか
// bがnumber型 type A = { a: string b: number } // bがstring型 type B = { b: string c: string }
この場合、
A | B
の時のb
はnumber | string
となり、number型かstring型を設定すれば良い
A & B
の場合は、コンパイルエラー(後述のプリミティブ型データの場合
を参照)
となります
プリミティブ型データの場合
最後にプリミティブ型の例です
type U = string | number type I = string & number
const us: U = '' // ok const ui: U = 0 // ok const ub: U = false // エラー const is: I = '' // エラー const ii: I = 0 // エラー const ib: I = false // エラー
ユニオン型は、分かりやすいですね
インターセクション型は、string 型と number 型の両方を表現するのは不可能なので、never 型になります
never 型は事前に起こることを想定してはいけない型なので、never 型の変数を定義し、何かを代入することは不可能です
つまり、プリミティブ型データのインターセクション型というものは定義できないということになります