メインコンテンツまでスキップ

infer

inferはConditional Typesの中で使われる型演算子です。inferは「推論する」という意味でextendsの右辺にのみ書くことができます。

ユーティリティ型ReturnType<T>の例からinferを知る

ある関数の戻り値の型を取得するユーティリティ型ReturnType<T>があります。ReturnType<T>は次のように定義されています。

ts
type ReturnType<T extends (...args: any) => any> = T extends (
...args: any
) => infer R
? R
: any;
ts
type ReturnType<T extends (...args: any) => any> = T extends (
...args: any
) => infer R
? R
: any;

試しに使ってみましょう。

ts
const request = (url: string): Promise<string> => {
return fetch(url).then((res) => res.text());
};
 
type X = ReturnType<typeof request>;
type X = Promise<string>
ts
const request = (url: string): Promise<string> => {
return fetch(url).then((res) => res.text());
};
 
type X = ReturnType<typeof request>;
type X = Promise<string>

typeofは変数から型を取得する演算子です。JavaScriptのtypeofとは異なるので注意してください。

📄️ typeof型演算子

TypeScriptのtypeofは変数から型を抽出する型演算子です。次は、変数pointにtypeof型演算子を用いて、Point型を定義する例です。このPoint型は次のような型になります。

このように関数requestの型から戻り値の型を取得することができました。

ReturnType<T>の解説

ReturnType<T>の構造を知るためにはまずT extends (...args: any) => anyが何かを知る必要があります。これは一般的な関数の型を示しています。任意の個数で任意の型の引数を受け取り、任意の型の値を返すことを示しています。Tは任意の関数を示しています。
そして戻り値の部分が=> infer R ? R : anyとなっており、Tが関数である場合は戻り値の型であるR、そうでない場合はanyを返すという意味になっています。
総合的にReturnType<T>Tが関数に割り当て可能である場合はR、そうでない場合はanyを返します。

inferを使うことによってある型Tが配列である場合はその要素の型、そうでない場合はneverを返すFlatten<T>を作ってみましょう。

ts
type Flatten<T> = T extends (infer U)[] ? U : never;
ts
type Flatten<T> = T extends (infer U)[] ? U : never;

このFlatten<T>を使ってみましょう。

ts
type A = Flatten<string>;
type A = never
type B = Flatten<string[]>;
type B = string
type C = Flatten<string[][]>;
type C = string[]
type D = Flatten<[string, number]>;
type D = string | number
ts
type A = Flatten<string>;
type A = never
type B = Flatten<string[]>;
type B = string
type C = Flatten<string[][]>;
type C = string[]
type D = Flatten<[string, number]>;
type D = string | number

2次元配列にFlatten<T>を適用すると1次元配列が返ってくることが、タプル型にFlatten<T>を適用するとユニオン型が返ってくることがわかります。