はじめに
「読んで学ぶTypeScript」をちょっとずつ読み進めます!
今回は、ユニオン型、インターセクションについてです!
コーディングに慣れていないので、ところどころ用語を調べて記載しながら書きますmm
そのため、初心者の方が理解するには、助けになるかもしれません ![]()
既存の型だけでは不便なことがある
TypeScriptには、stringやnumberなどのプリミティブ型以外に、それらを組み合わせてより複雑なデータを表現する機能があるらしいです。
今回はユニオン型・インターセクション型ついて学びます ![]()
ユニオン型とは
- ユニオン型(union type)は「いずれかの型」を持つ
- 「OR」の考え方
- 2つ以上の型を
|で繋げて書く- 例:number型もしくはundefined型 =
number | undefined
- 例:number型もしくはundefined型 =
- 配列にユニオン型を使う時は、
()で囲う
// 配列にユニオン型を使いたい時のNG例
type List = string | number[];
// 「単一の文字列 (string)」または「数値のみの配列 (number[])」という意味になる
let listC: List = ["Hello", 10, 20];
// これは「文字列と数値が混ざった配列」なので、上記のどちらの型にも合致せず、エラーになる
// 配列にユニオン型を使いたい時のOK例
type List = (string | number)[];
// 配列の要素の型が、文字列か数値のどちらかであるという意味になる
let listC: List = ["Hello", 10, 20];
// 文字列と数値を混ぜてもOK(文字列だけの配列でも、数値だけの配列でもOK)
判別可能なユニオン型
- 通常のユニオン型だと、中身の型がごちゃ混ぜになっていて、判断するために複雑な分岐が必要になる
- 判別可能なユニオン型は、各オブジェクトの型を判別するためのプロパティ(しるし)を持っている
- このプロパティを ディスクリミネータ(discriminator) と呼ぶ
判別可能なユニオン型が便利な例
// UploadStatus - ファイルアップロードの状況を表現したユニオン型
type UploadStatus = InProgress | Success | Failure;
type InProgress = { done: boolean; progress: number };
type Success = { done: boolean };
type Failure = { done: boolean; error: Error };
| 型 | 意味 | done | progress | error |
|---|---|---|---|---|
| InProgress | アップロード中 | false | 進捗率(%) | - |
| Success | アップロード成功 | true | - | - |
| Failure | アップロード失敗 | true | - | エラー詳細 |
上記の状態を判断して表示させる関数の実装の場合、判別可能になっていないユニオン型だと、
function printStatus(status: UploadStatus) {
if (status.done === false) {
console.log(`アップロード中:${status.progress}%`);
// エラーになる(progressプロパティが、type Success,Failureにはない)
}
}
-
status.done === falseで、InProgressに絞っているはず - しかし、コンパイラーは if分岐内で、statusがSuccessやFailureかもしれないと考えるため、エラーになる
- 解消するには、progressがあることをチェックする必要がある↓
function printStatus(status: UploadStatus) {
if (status.done === false && "progress" in status) {
// ^^^^^^^^^^^^^^^^^^^^追加
console.log(`アップロード中:${status.progress}%`);
// コンパイルエラーが解消!
}
}
- このチェックを全ての状態で挟むと、コードが冗長になる...
- そこで、ディスクリミネータ(type)を追加して、InProgress,Success,Failureのどの状態か判別できるようにする
| 型 | 意味 | ディスクリミネータ | progress | error |
|---|---|---|---|---|
| InProgress | アップロード中 | type: "InProgress" | 進捗率(%) | - |
| Success | アップロード成功 | type: "Success" | - | - |
| Failure | アップロード失敗 | type: "Failure" | - | エラー詳細 |
function printStatus(status: UploadStatus) {
if (status.type === "InProgress") {
console.log(`アップロード中:${status.progress}%`);
} else if (status.type === "Success") {
console.log("アップロード成功", status);
} else if (status.type === "Failure") {
console.log(`アップロード失敗:${status.error.message}`);
} else {
console.log("不正なステータス: ", status);
}
}
ディスクリミネータを追加すると、分岐処理が読みやすく、保守性も高くなる!
インターセクション型とは
- インターセクション型(intersection type)は一つの値が複数の型を持つことができる
- 「AND」の考え方
- 既存の型を結合して、新たな型を作る際に便利
- 合成したいオブジェクト同士を
&で列挙して書く
// 例
type Name = {
name: string
}
type Age = {
age: number
}
type Person = Name & Age
// ^^^インターセクション型
const person: Person = {
name: 'John',
age: 20,
}
インターセクション型が便利な例
// パラメーターがめちゃくちゃ増えた...
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>: 全プロパティを必須にする
type Mandatory = Required<{
id: string;
active: boolean;
balance: number;
surname: string;
givenName: string;
email: string;
}>;
// Partial<T>: 全プロパティをオプショナルにする
type Optional = Partial<{
index: number;
photo: string;
age: number;
company: string;
phoneNumber: string;
address: string;
}>;
// インターセクション型で合体
type Parameter = Mandatory & Optional;
既存の型を壊さずに機能を追加できて、より柔軟な型定義が可能になる!
さいごに
次は、型エイリアスや型アサーションについて読んでみようと思います!
アドカレ2025が開催中!
今年もアドカレ開催中です ![]()
面白そうなカレンダーがたくさんです!
特設サイト ↓
ギフトカードや Airpods, iPadなど、豪華賞品すぎるプレゼントカレンダーは必見!
↓
参考