TypeScriptのinferを使ってみる
TypeScriptの型システムはとても強力で、コードの安全性や明瞭性を向上させるための様々な機能が備わっています。その中でも infer
キーワードは、条件型の中で型の一部を動的に抽出するのに役立ちます。この記事では、infer
の基本的な使い方から実践的な例まで、幅広く解説していきます。
1. inferって何?
infer
は、TypeScriptの条件型の中で使われるキーワードです。
具体的には、ある型から内部にある型情報を抜き出したい場合に使用します。
たとえば、配列やPromise、関数の戻り値など、ある型の「中身」を取り出すのに最適です。
基本の構文:
type Extracted<T> = T extends SomeType<infer U> ? U : DefaultType;
-
T extends SomeType<infer U>
→ もしT
がSomeType<...>
の形なら、infer U
によりその内部の型をU
として抽出します。 -
? U : DefaultType
→ 条件が真ならU
を、偽ならDefaultType
を返します。
2. inferを使った具体例
2.1 配列の要素型を抽出する
例えば、配列の型から、その要素の型だけを取り出すユーティリティ型。
// 配列の要素型を抽出する型
type ElementType<T> = T extends (infer U)[] ? U : T;
// 使用例
type Numbers = number[];
type NumberElement = ElementType<Numbers>; // number
type Strings = string[];
type StringElement = ElementType<Strings>; // string
// 非配列の場合はそのままの型が返る
type NotArray = boolean;
type NotArrayResult = ElementType<NotArray>; // boolean
解説:
T extends (infer U)[]
の部分で、もし T
が配列型であれば、配列の要素の型を U
として抽出します。
もし T
が配列でなければ、T
自体を返す仕組みになっています。
2.2 Promiseの解決値を抽出する
Promiseが返す値の型を抽出する方法も、infer
を使って簡単に実装できます。
// Promiseの解決値の型を抽出する型
type Awaited<T> = T extends Promise<infer U> ? U : T;
// 使用例
type PromiseString = Promise<string>;
type ResolvedString = Awaited<PromiseString>; // string
type NonPromise = number;
type NonPromiseResult = Awaited<NonPromise>; // number
解説:
T extends Promise<infer U>
として、もし T
がPromise型ならその解決値の型を U
として抽出します。
Promiseでなければ、T
自体がそのまま返されます。
2.3 関数の戻り値の型を抽出する
関数の戻り値型を取り出すユーティリティ型も、infer
を利用して実装できます。
// 関数の戻り値の型を抽出する型
type ReturnTypeInfer<T> = T extends (...args: any[]) => infer R ? R : never;
// 使用例
type Fn = (x: number, y: number) => string;
type FnReturn = ReturnTypeInfer<Fn>; // string
type NotFunction = number;
type NotFunctionReturn = ReturnTypeInfer<NotFunction>; // never
解説:
T extends (...args: any[]) => infer R
の部分で、もし T
が関数型ならば、戻り値の型を R
として抽出します。
もし T
が関数でなければ never
を返すようにしています。
3. inferのメリットと使いどころ
メリット
- 柔軟性: 型定義の中から動的に内部の型を抽出できるため、再利用性の高いユーティリティ型を簡単に作成できます。
- 型安全性: 型の一部を正確に取り出すことで、意図しない型のミスマッチを防ぎます。
- 可読性の向上: 複雑な型の操作をシンプルに表現でき、コード全体の可読性が向上します。
使いどころ
- ライブラリ作成: 汎用的な型ユーティリティを作成してライブラリ化する際に有用です。
- APIレスポンスの型変換: サーバーから返ってくるデータの型から、必要な部分だけを取り出す場合に活躍します。
- 高度な型操作: 型レベルのロジックを記述する際に、柔軟に型を操作できるため、複雑なシステムでも型安全性を担保できます。
4. まとめ
infer
を使えば、ある型から一部を取り出して再利用することができ、コードの柔軟性と安全性を向上させることができます。
ただ、正直あまり使う機会はないかもしれません。
私は現場で3年ほどTypeScriptを使用した経験がありますが、あまり見ないですし、使いたいユースケースに遭遇したこともありません、、APIレスポンスの型変換は便利かもしれませんが。
どちらかと言うとライブラリ開発者が使用する機能なのかもしれません。
初心者の方には、こんなこともできるんだなぐらいに頭の片隅に入れておくぐらいで良いかもしれません。
たまにinferを見かけた時に使い方が気になっていたので今回記事にしました。
Discussion