🚥
【TypeScript】条件型とinferキーワードを理解する
条件型とは
条件型は、与えられた型に応じて別の型を生成する機能です。構文は次のようなものです。
T extends U ? A : B
T
がU
のサブタイプであればA
を、そうでなければB
を割り当てます。
三項演算子と同じことを型レベルで行っていると考えるとわかりやすいですね。
条件型利用例
type isString<T> = T extends string // Tはstringのサブタイプですか?
? true // そうであれば true
: false // そうでなければ false
type A = isString<string> // true
type B = isString<number> // false
例のような型エイリアスだけでなく、インターフェース、クラス、パラメーター型、関数、メソッド内のジェネリックのデフォルト型の中で条件型を使うことができます。
分配条件型
条件型は数学の分配法則のような表現ができます。
(string | number) extends T ? A : B
// ↓これと等しい
(string extends T ? A : B) | (number extends T ? A : B)
inferキーワード
inferキーワードはT extends U ? A : B
のUの中の型を参照できるようにするために使います。
配列の要素の型を取得するinferを使った条件型
(T[number]はTの要素の型を返します)
// inferを使わない条件型
type ElementType1<T> = T extends unknown[] // Tが配列だったら
? T[number] // Tの配列の要素の型を返す
: T // そうでなければTを返す
type A = ElementType1<number[]> // number
// 上と同じものをinferを使って書くと
type ElementType2<T> = T extends (infer U)[] // Tが配列だったら(要素の型をUと置く)
? U // Uを返す
: T // そうでなければTを返す
type A = ElementType2<number[]> // number
組み込みの条件型
TypeScriptにはグローバルに利用可能な組み込みの条件型が用意されています。
Exclude<T,U>
T
に含まれるがU
には含まれない型
(T
からU
を除く)
type A = Exclude<number | string , string> // number
条件型によるExcludeの定義
type Exclude<T, U> = T extends U ? never : T;
Extract<T,U>
T
に含まれる型のうち、U
割り当てることのできる型
(T
からU
に含まれる型のみを抽出)
type A = Extract<number | string , string> // string
条件型によるExtractの定義
type Extract<T, U> = T extends U ? T : never;
NonNullable<T>
Tのnullとundefinedを除いた型
type A = {
a: number | null
}
type B = NonNullable<A['a']> // number
条件型によるNonNullabeの定義
type NonNullable<T> = T extends null | undefined ? never : T
ReturnType<F>
関数の戻り値の型
(ジェネリックやオーバーロードされた関数はうまく動作しない場合があります。)
type A = ReturnType<(a:number) => string> // string
条件型によるReturnTypeの定義
type ReturnType<T extends (...args: any[]) => any> = T extends (...args: any[]) => infer R ? R : any;
InstanceType<C>
クラスコンストラクターのインスタンスの型
// 例1
type A = {new(): B}
type B = {b: number}
type I = InstanceType<A> // {b:number}
// 例2
class Person {
//...
}
type PersonType = InstanceType<typeof Person> // Person
条件型によるInstanceTypeの定義
type InstanceType<T extends new (...args: any[]) => any> = T extends new (...args: any[]) => infer R ? R : any;
利用例がイメージしにくいですが、クラス定義にアクセスできない・クラス定義が複雑すぎる場合などに使うことができます。
Discussion