TypeScriptのinferってなんなん?
TypeScriptのinferって?
inferは、TypeScriptの条件付き型(T extends U ? X : Y
の構文)で登場するキーワードで、型を「推論(infer)」するためのものです。
具体的には、条件付き型の中で「T
の一部を別名として取り出し、その新たに名付けた型を後続で使用する」ために利用されます。
inferは、条件付き型の中でしか使えないという制限があるのも特徴です。
主な用途
inferが活用される場面は大きく以下の2つが代表的です。
1. 型の一部を抽出
ある型の部分的な構造や要素型を取り出して使う際に便利です。たとえば、関数型の返り値だけを取り出したり、配列の要素型を取り出したりするときに使われます。
2. 特定の型の中身を推論して使う
ライブラリなどでジェネリクス(総称型)を多用する際、条件付きで「もしこういう型なら、その中身を推論して型パラメータに使う」といった複雑な型定義に活用できます。例えば、既存のユーティリティ型(ReturnType<T>, Parameters<T>, ConstructorParameters<T>
など)は、内部でinferを使って実装されています。
基本の使い方
例:関数の戻り値の型を取り出す
TypeScriptの標準ライブラリには、すでにReturnType<T>
というユーティリティ型が定義されています。その仕組みを簡略化すると以下のようになります。
type ReturnType<T> = T extends (...args: any[]) => infer R ? R : never;
T
が関数型((...args: any[]) => something
)である場合、infer R
によって「戻り値の型をRとして推論する」という動作を行います。
もしT
が関数型でなければ、never
を返します。
利用イメージとしては、以下のようになります。
type MyFunction = () => string;
type Result = ReturnType<MyFunction>; // string になる
MyFunctionという関数型(戻り値がstring)を渡すと、Resultはstringとして推論されます。
もう少し実践的な例
例:配列の要素の型を取り出す
inferが使える典型例のひとつとして、配列の要素型を取り出すユーティリティ型を考えてみます。
type ElementType<T> = T extends (infer U)[] ? U : never;
ElementType<number[]>
とした場合、infer U
の部分が「もしT
がU[]
形式なら、その要素型をU
として推論する」という形で働き、結果はnumber
となります。
type Example = ElementType<number[]>; // number
さらに、T``に配列型でないものを与えた場合は、never
になり、要素が存在しない型として扱う、という形にもできます。
補足:より複雑な使い方
inferは、より複雑な条件付き型の文脈でも登場します。たとえば「タプル型の先頭要素と残りの要素に分解して何かをしたい」場合や、複数のinferを使ったり、ネストした条件付き型の中で使われるケースもあります。実際、TypeScriptが提供する他のユーティリティ型(Parameters<T>, ConstructorParameters<T>, InstanceType<T>
など)も、内部的に似たような構造を使っており、関数の引数型やクラスのインスタンス型を推論して取り出すためにinferを活用しています。
Parameters<T>: 関数型Tの引数リストをタプル型として取得
ConstructorParameters<T>: コンストラクタの引数をタプル型として取得
InstanceType<T>: クラス(コンストラクタ関数)のインスタンスの型を取得
いずれも中身を見ると、条件付き型とinferで型を推論しているのが分かります。
気をつけたいポイント
条件付き型の中でしか使えない
infer R
の構文は必ずextends ? :
の中で使う必要があります。type Foo = infer R;
のように単独で使用することはできません。
型パラメータ(ジェネリクス)が多くなると読みづらくなりがち
複雑な型の推論ロジックを書くと、どうしても可読性が低下する場合があります。必要に応じて型エイリアスを定義して分割したり、コメントを添えたりしてわかりやすく整理するのが大事です。
複数のinferを使うと推論順序に注意
ひとつの条件式の中で複数のinferを行う場合、推論の順序や互いの依存関係で想定外のエラーが出ることがあります。必要に応じて条件式を分割し、段階的に推論を行うとトラブルを回避できます。
まとめ
- inferは条件付き型
(extends ? :)
の中で使うキーワード - 型を部分的に「推論」し、それを変数のように扱える。
- 関数の戻り値や配列の要素型などを取り出す用途に便利
- 自作ユーティリティ型や、既存のユーティリティ型の実装に多用されている。
- 複雑なジェネリクスの型を扱うとき強い威力を発揮する
- ライブラリなどではinferを使って、型の一部を柔軟に抽出・再活用することがよくある。
最初はややとっつきにくいですが、「型レベルでのモジュール化・再利用」に役立ちます。
関数の引数や戻り値、クラスのインスタンスなど、実際のコードで活用例を眺めてみるとイメージが掴みやすくなるでしょう。
ぜひ活用して、型定義の表現力を高めてみてください。
Discussion