メインコンテンツまでスキップ

インターセクション型 (intersection type)

考え方はユニオン型と相対するものです。ユニオン型がどれかを意味するならインターセクション型はどれもです。言い換えるとオブジェクトの定義を合成させることを指します。

インターセクション型を作るためには合成したいオブジェクト同士を&で列挙します。

typescript
type TwoDimensionalPoint = {
x: number;
y: number;
};
type Z = {
z: number;
};
type ThreeDimensionalPoint = TwoDimensionalPoint & Z;
const p: ThreeDimensionalPoint = {
x: 0,
y: 1,
z: 2,
};
typescript
type TwoDimensionalPoint = {
x: number;
y: number;
};
type Z = {
z: number;
};
type ThreeDimensionalPoint = TwoDimensionalPoint & Z;
const p: ThreeDimensionalPoint = {
x: 0,
y: 1,
z: 2,
};

xy平面上の点を表すTwoDimensionalPointを拡張してxyz平面上の点のThreeDimensionalPointに変換しました。

プリミティブ型のインターセクション型#

プリミティブ型のインターセクション型をつくることもできますが、作るとneverという型ができます。

typescript
type Never = string & number;
const n: Never = "2";
// Type '"2"' is not assignable to type 'never'.
typescript
type Never = string & number;
const n: Never = "2";
// Type '"2"' is not assignable to type 'never'.

このnever型にはいかなる値も代入できません。使い道がまるでないように見えますが意外なところで役に立ちます。今回は説明を省きます。

インターセクション型を使いこなす#

システムの巨大化に伴い、受け付けたいパラメーターが巨大化したとします。

typescript
type Parameter = {
id: string;
index?: number;
active: boolean;
balance: number;
photo?: string;
age?: number;
surname: string;
givenName: string;
company?: string;
email: string;
phoneNumber?: string;
address?: string;
// ...
};
typescript
type Parameter = {
id: string;
index?: number;
active: boolean;
balance: number;
photo?: string;
age?: number;
surname: string;
givenName: string;
company?: string;
email: string;
phoneNumber?: string;
address?: string;
// ...
};

一見してどのプロパティが必須で、どのプロパティが選択可かが非常にわかりづらいです。これをインターセクション型とユーティリティ型のRequired<T>Partial<T>を使いわかりやすく表記できます。ユーティリティ型については解説しているページがありますのでご覧ください。

必須とそうでないパラメータのタイプエイリアスに分離する#

typescript
type Mandatory = {
id: string;
active: boolean;
balance: number;
surname: string;
givenName: string;
email: string;
};
type Optional = {
index: number;
photo: string;
age: number;
company: string;
phoneNumber: string;
address: string;
};
typescript
type Mandatory = {
id: string;
active: boolean;
balance: number;
surname: string;
givenName: string;
email: string;
};
type Optional = {
index: number;
photo: string;
age: number;
company: string;
phoneNumber: string;
address: string;
};

Required<T>, Partial<T>をつける#

MantatoryRequired<T>を、OptionalPartial<T>をつけます。

typescript
type Mandatory = Required<{
id: string;
active: boolean;
balance: number;
surname: string;
givenName: string;
email: string;
}>;
type Optional = Partial<{
index: number;
photo: string;
age: number;
company: string;
phoneNumber: string;
address: string;
}>;
typescript
type Mandatory = Required<{
id: string;
active: boolean;
balance: number;
surname: string;
givenName: string;
email: string;
}>;
type Optional = Partial<{
index: number;
photo: string;
age: number;
company: string;
phoneNumber: string;
address: string;
}>;

インターセクション型で合成する#

これで最初に定義したParameterと同じタイプエイリアスができました。

typescript
type Parameter = Readonly<Mandatory & Optional>;
typescript
type Parameter = Readonly<Mandatory & Optional>;