今回は、TypeScriptの条件型についてまとめます。
条件型とは
TypeScriptの条件型とは、型システムに条件分岐を導入するものです。
下記の構文で表現され、
「T
が U
に割り当て可能であれば X
、そうでなければ Y
」という意味になります。
T extends U ? X : Y
例えば下記の場合、T
に入る値が string
の場合に true
、そうでない場合に false
を返します。
type IsString<T> = T extends string ? true : false;
type Result1 = IsString<"hello">; // true
type Result2 = IsString<123>; // false
分配条件型
分配条件型は、T
がユニオン型である場合に、ユニオン型の各要素に対して条件型が個別に適用される仕組みです、
type Distributive<T> = T extends string ? "String" : "Other";
type Result = Distributive<"hello" | 42 | true>;
// Result = "Other" | "String"
ユニオン型から特定の型だけを抽出することもできます。
type ExtractString<T> = T extends string ? T : never;
type OnlyStrings = ExtractString<"hello" | 42 | "world">;
// OnlyStrings = "hello" | "world"
Inferキーワード
条件型には、ジェネリック型をインラインで宣言する独自構文があり、それが infer
です。
下記の構文で使用され、
T
が Array
型に一致する場合、infer U
を使用して配列の要素型を U
として推論しています。
それ以外の場合は never
が返されます。
type InferExample<T> = T extends Array<infer U> ? U : never;
下記の例では配列の要素型を抽出しています。
type ElementType<T> = T extends Array<infer U> ? U : never;
type StringArray = ElementType<string[]>; // StringArray = string
type NumberArray = ElementType<number[]>; // NumberArray = number
type NonArray = ElementType<boolean>; // NonArray = never
関数の引数型や戻り値の型を抽出することもできます。
type ReturnType<T> = T extends (...args: any[]) => infer R ? R : never;
type ArgumentType<T> = T extends (arg: infer A) => any ? A : never;
type MyFunction = (x: number) => string;
type Result = ReturnType<MyFunction>; // Result = string
type Arg = ArgumentType<MyFunction>; // Arg = number
組み込みの条件型
TypeScriptには、型を操作するための便利な組み込みユーティリティ型がいくつか用意されています。
Exclude<T, U>
Exclude<T, U>
は、T
には含まれているが U
には含まれていない型を計算します。
type Exclude<T, U> = T extends U ? never : T;
type Union = "a" | "b" | "c";
type WithoutA = Exclude<Union, "a">;
// WithoutA = "b" | "c"
Extract<T, U>
Extract<T, U>
は、T
には含まれている型のうち、U
に割り当てできるものを計算します。
type Extract<T, U> = T extends U ? T : never;
type Union = "a" | "b" | "c";
type OnlyA = Extract<Union, "a">;
// OnlyA = "a"
NonNullable<T>
NonNullable<T>
は、null
や undefined
を型から除外するために使用します。
type NonNullable<T> = T extends null | undefined ? never : T;
type NullableType = string | null | undefined;
type NonNull = NonNullable<NullableType>;
// NonNull = string
ReturnType<F>
ReturnType<F>
は、関数の戻り値の型を計算します。
F
は型を取得したい関数を表しています。
type ReturnType<F> = F extends (...args: any[]) => infer R ? R : any;
function getUser() {
return { name: "John", age: 30 };
}
type User = ReturnType<typeof getUser>;
// User = { name: string; age: number }
InstanceType<C>
InstanceType<C>
は、クラスのインスタンス型を計算します。
C
は型を取得したいクラス名を表しています。
type InstanceType<C extends new (...args: any) => any> = C extends new (...args: any) => infer R ? R : never;
class Person {
name: string;
constructor(name: string) {
this.name = name;
}
}
type PersonInstance = InstanceType<typeof Person>;
// PersonInstance = Person
おわりに
条件型をしようすることで柔軟にカスタマイズでき、
コードも簡潔に記述できるので非常に便利だと思いました。
それでは。
参考文献