高度な型
TypeScript
Union、Intersection、Conditional Types
Union / Intersection 型
| と & を使った型の合成
// Union型(どちらかの型)
type ID = string | number;
type Result<T> = T | Error;
type Theme = 'light' | 'dark' | 'system';
// 型ガードで絞り込み
function processId(id: ID) {
if (typeof id === 'string') {
id.toUpperCase(); // string 確定
} else {
id.toFixed(0); // number 確定
}
}
// Discriminated Union(タグ付きUnion)
type Shape =
| { kind: 'circle'; radius: number }
| { kind: 'rectangle'; width: number; height: number }
| { kind: 'triangle'; base: number; height: number };
function area(shape: Shape): number {
switch (shape.kind) {
case 'circle': return Math.PI * shape.radius ** 2;
case 'rectangle': return shape.width * shape.height;
case 'triangle': return (shape.base * shape.height) / 2;
}
}
// Intersection型(両方の型を持つ)
type WithId = { id: number };
type WithTimestamp = { createdAt: Date; updatedAt: Date };
type Entity = WithId & WithTimestamp;
// Entity = { id: number; createdAt: Date; updatedAt: Date }条件型
T extends U ? X : Y パターン
// 基本構文: T extends U ? TrueType : FalseType
type IsString<T> = T extends string ? true : false;
type A = IsString<string>; // true
type B = IsString<number>; // false
// Distribution(Unionに対して分配)
type ToArray<T> = T extends unknown ? T[] : never;
type C = ToArray<string | number>; // string[] | number[]
// NonNullable の実装
type NonNullable<T> = T extends null | undefined ? never : T;
// infer — 型の推論
type ElementType<T> = T extends (infer E)[] ? E : never;
type D = ElementType<string[]>; // string
type UnpackPromise<T> = T extends Promise<infer R> ? R : T;
type E = UnpackPromise<Promise<User>>; // User
type F = UnpackPromise<string>; // string
// 深いオプショナル(再帰的条件型)
type DeepPartial<T> = T extends object
? { [K in keyof T]?: DeepPartial<T[K]> }
: T;Mapped Types
既存の型のプロパティをマッピング
// 基本構文: { [K in keyof T]: ... }
type Stringify<T> = { [K in keyof T]: string };
type Nullable<T> = { [K in keyof T]: T[K] | null };
// +? / -? で省略可能を追加・削除
type AllOptional<T> = { [K in keyof T]+?: T[K] };
type AllRequired<T> = { [K in keyof T]-?: T[K] };
// readonly の追加・削除
type Freeze<T> = { +readonly [K in keyof T]: T[K] };
type Mutable<T> = { -readonly [K in keyof T]: T[K] };
// キーの再マッピング(as)
type Getters<T> = {
[K in keyof T as `get${Capitalize<string & K>}`]: () => T[K];
};
// { getName: () => string; getAge: () => number; ... }
// フィルタリング
type PickByValue<T, V> = {
[K in keyof T as T[K] extends V ? K : never]: T[K];
};
type StringProps = PickByValue<User, string>; // name: string; email: string;