【初心者向け】TypeScript ユニオン型 - 基本から応用まで徹底解説
TypeScriptにおけるユニオン型のすべての可能なキーを理解する
そこで今回は、TypeScriptにおけるユニオン型のすべての可能なキーを理解するための包括的なガイドを提供します。
型定義の確認
まず、分析対象のユニオン型の定義を確認する必要があります。型定義には、構成する各オブジェクト型とそのプロパティが記載されています。
type UserOrAdmin = {
name: string;
age: number;
} | {
name: string;
email: string;
role: string;
};
この例では、UserOrAdmin
型は、User
型と Admin
型のユニオン型です。
共通プロパティの特定
ユニオン型に属するすべてのオブジェクト型が共有するプロパティは、すべての可能なキーとなります。上記の例では、name
プロパティは User
型と Admin
型で共通なので、UserOrAdmin
型のすべての可能なキーとなります。
const userOrAdmin: UserOrAdmin = {
name: "John Doe",
// age または email と role のどちらか一方のみ存在
};
型ガードによる絞り込み
ユニオン型の中には、判別型と呼ばれる、各オブジェクト型を区別するためのプロパティを持つものがあります。判別型を用いることで、条件分岐を行い、特定のオブジェクト型に属するキーのみを抽出することができます。
type UploadStatus = "pending" | "success" | "failure";
function getStatusDetails(status: UploadStatus): string {
switch (status) {
case "pending":
return "アップロード中";
case "success":
return "アップロード完了";
case "failure":
return "アップロード失敗";
}
}
この例では、UploadStatus
型は "pending"
, "success"
, "failure"
のいずれかの値を持つ判別型です。getStatusDetails
関数では、status
の値に基づいて、それぞれのステータスに対応したメッセージを返します。
高度な型操作ライブラリの活用
type UserOrAdmin = {
name: string;
age?: number; // オプション
} | {
name: string;
email: string;
role: string;
};
function printCommonProperties(userOrAdmin: UserOrAdmin) {
console.log(userOrAdmin.name); // name プロパティは常に存在
if (userOrAdmin.age) {
console.log(userOrAdmin.age); // age プロパティはオプション
}
}
判別型を用いた絞り込み
type UploadStatus = "pending" | "success" | "failure";
function handleUploadStatus(status: UploadStatus) {
switch (status) {
case "pending":
console.log("アップロード中...");
break;
case "success":
console.log("アップロード完了!");
break;
case "failure":
console.error("アップロード失敗!");
break;
}
}
type Product = {
id: number;
name: string;
price: number;
stock: number;
};
type DiscountedProduct = Product & {
discount: number;
};
function getAvailableProductKeys(product: Product | DiscountedProduct): (keyof Product | keyof DiscountedProduct)[] {
return [...Object.keys(product)]; // keyof 演算子で抽出
}
const productOrDiscountedProduct: Product | DiscountedProduct = {
id: 1,
name: "T-Shirt",
price: 1000,
stock: 10,
// discount プロパティはオプション
};
const availableKeys = getAvailableProductKeys(productOrDiscountedProduct);
console.log(availableKeys); // ["id", "name", "price", "stock", "discount"]
複数の型ガードを組み合わせることで、より複雑な条件に基づいてキーを絞り込むことができます。
type UserOrAdmin = {
name: string;
age?: number;
email?: string;
role?: string;
};
function isAdmin(userOrAdmin: UserOrAdmin): userOrAdmin is { role: string } {
return typeof userOrAdmin.role === "string";
}
function printAdminDetails(userOrAdmin: UserOrAdmin) {
if (isAdmin(userOrAdmin)) {
console.log(userOrAdmin.name);
console.log(userOrAdmin.role);
}
}
ジェネリック型による再帰
ジェネリック型を用いて、再帰的にユニオン型のすべての可能なキーを抽出するコードを書くことができます。
type UnionToIntersection<U> = (
& {[K in keyof U]: (u: U) => Pick<U, K>[K]}
)[U] extends infer I ? I : never;
type GetAllKeys<T> = UnionToIntersection<T extends object ? T : never>;
type UserOrAdminKeys = GetAllKeys<UserOrAdmin>; // "name" | "age" | "email" | "role"
サードパーティ製ライブラリの利用
ts-nameof
や @types/utility-types
などのサードパーティ製ライブラリの中には、ユニオン型のすべての可能なキーを取得するためのユーティリティ関数を提供するものがあります。
import { Keys } from "ts-nameof";
type UserOrAdminKeys = Keys<UserOrAdmin>; // "name" | "age" | "email" | "role"
typescript