TypeScript:型システムを理解して使いこなす - ユニオントイプからインターセクションタイプへの変換
TypeScriptでユニオントイプをインターセクションタイプに変換する方法
ユニオントイプ は、複数の型を |
演算子で結合した型です。例えば、string | number
型は、文字列型または数値型のいずれかの値を持つことができます。
一方、インターセクションタイプ は、複数の型を &
演算子で結合した型です。例えば、{ name: string } & { age: number }
型は、name
プロパティを持つ文字列型と、age
プロパティを持つ数値型の両方の性質を持つオブジェクト型となります。
この解説では、ユニオントイプをインターセクションタイプに変換する方法について、いくつかの例を用いて説明します。
手動による変換
最も単純な方法は、それぞれの型を個別にインターセクション型で記述することです。
type UnionType = string | number;
type IntersectionType = {
stringValue: UnionType extends string ? string : never;
numberValue: UnionType extends number ? number : never;
};
この例では、UnionType
型は string
または number
型のいずれかですが、IntersectionType
型は stringValue
プロパティと numberValue
プロパティを持ち、それぞれ string
型と number
型であることが保証されます。
typeof 演算子を使う
typeof
演算子を使うと、変数や型の情報に基づいて新しい型を生成できます。
const unionValue: UnionType = "hello";
type IntersectionType = typeof unionValue extends string ? { stringValue: string } : { numberValue: number };
この例では、unionValue
変数の型が string
型であることが分かっているので、IntersectionType
型は stringValue
プロパティを持つ string
型になります。
ユーティリティライブラリを使う
いくつかのユーティリティライブラリは、ユニオントイプをインターセクションタイプに変換する機能を提供しています。
例:typescript-transform-union-to-intersection
ライブラリ
import { UnionToIntersection } from "typescript-transform-union-to-intersection";
type UnionType = string | number;
type IntersectionType = UnionToIntersection<UnionType>;
このライブラリを使うと、UnionToIntersection
型を使って、UnionType
型を IntersectionType
型に変換できます。
ユニオントイプをインターセクションタイプに変換する方法はいくつかあります。それぞれの手法にはメリットとデメリットがあり、状況に応じて使い分けることが重要です。
- ユーティリティライブラリを使う:汎用性が高いが、ライブラリの導入が必要
typeof
演算子を使う:簡潔に記述できるが、型情報の制限がある- 手動による変換:最も柔軟性が高いが、記述量が多くなる
// ユニオントイプ
type UnionType = string | number;
// インターセクションタイプ
type IntersectionType = {
stringValue: UnionType extends string ? string : never;
numberValue: UnionType extends number ? number : never;
};
// 使用例
function logValue(value: IntersectionType) {
if ("stringValue" in value) {
console.log("文字列値:", value.stringValue);
} else if ("numberValue" in value) {
console.log("数値値:", value.numberValue);
}
}
const value1: IntersectionType = { stringValue: "hello" };
logValue(value1); // 出力: "文字列値: hello"
const value2: IntersectionType = { numberValue: 123 };
logValue(value2); // 出力: "数値値: 123"
// ユニオントイプ
type UnionType = string | number;
// 変数
const unionValue: UnionType = "hello";
// インターセクションタイプ
type IntersectionType = typeof unionValue extends string ? { stringValue: string } : { numberValue: number };
// 使用例
function logValue(value: IntersectionType) {
if ("stringValue" in value) {
console.log("文字列値:", value.stringValue);
} else if ("numberValue" in value) {
console.log("数値値:", value.numberValue);
}
}
logValue(unionValue); // 出力: "文字列値: hello"
// ライブラリのインポート
import { UnionToIntersection } from "typescript-transform-union-to-intersection";
// ユニオントイプ
type UnionType = string | number;
// インターセクションタイプ
type IntersectionType = UnionToIntersection<UnionType>;
// 使用例
function logValue(value: IntersectionType) {
if ("stringValue" in value) {
console.log("文字列値:", value.stringValue);
} else if ("numberValue" in value) {
console.log("数値値:", value.numberValue);
}
}
const value: IntersectionType = "hello";
logValue(value); // 出力: "文字列値: hello"
ジェネリック型を使う
ジェネリック型を使うと、ユニオントイプの各要素に対して個別にインターセクションタイプを作成できます。
type UnionToIntersection<T> = (
T extends any ? (x: T) => void : never
) extends (x: infer U) => void
? U
: never;
type UnionType = string | number;
type IntersectionType = UnionToIntersection<UnionType>;
// 使用例
function logValue(value: IntersectionType) {
if ("stringValue" in value) {
console.log("文字列値:", value.stringValue);
} else if ("numberValue" in value) {
console.log("数値値:", value.numberValue);
}
}
const value: IntersectionType = "hello";
logValue(value); // 出力: "文字列値: hello"
keyof
演算子を使って、ユニオントイプのすべてのプロパティ名を取得し、それらを基にインターセクションタイプを作成できます。
type UnionType = {
stringValue: string;
} | {
numberValue: number;
};
type IntersectionType = {
[K in keyof UnionType]: UnionType[K];
};
// 使用例
function logValue(value: IntersectionType) {
if ("stringValue" in value) {
console.log("文字列値:", value.stringValue);
} else if ("numberValue" in value) {
console.log("数値値:", value.numberValue);
}
}
const value: IntersectionType = { stringValue: "hello" };
logValue(value); // 出力: "文字列値: hello"
マージ型を使う
マージ型を使うと、複数の型を単一の型に結合できます。
type UnionType = string | number;
type IntersectionType = {
stringValue?: string;
numberValue?: number;
} & UnionType;
// 使用例
function logValue(value: IntersectionType) {
if ("stringValue" in value) {
console.log("文字列値:", value.stringValue);
} else if ("numberValue" in value) {
console.log("数値値:", value.numberValue);
}
}
const value: IntersectionType = { stringValue: "hello" };
logValue(value); // 出力: "文字列値: hello"
これらの方法は、それぞれ異なる利点と欠点があります。状況に応じて適切な方法を選択することが重要です。
typescript