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 型の変数を定義し、何かを代入することは不可能です
つまり、プリミティブ型データのインターセクション型というものは定義できないということになります