質問の背景
TypeScriptの発展に伴い、特に関連する関数が多数存在する大規模なプロジェクトにおいて、関数の返り値の型を自動的に抽出する必要性が生まれました。このため、TypeScript 2.8以降に標準ライブラリに追加されたユーティリティタイプReturnType<T>が導入されました。
問題
大規模で複雑なプロジェクトでは、型を最新の状態に保つことが難しくなります。関数の返り値の型を手動で指定し、シグネチャを変更すると、シグネチャと実装が一致しなくなることが容易にあります。さらに、関数が複雑な構造を返す場合、すべての使用箇所で返り値の型の説明を手動で同期させることは必ずしも簡単ではありません。
解決策
ユーティリティタイプReturnType<T>は、関数の返り値の型を自動的に抽出し、コードの任意の場所で関数呼び出しの結果を型指定するために使用できます。これにより、型インフラの手動サポートの量が減り、返り値の型の記述と実際の型との不一致に関連するエラーが最小限に抑えられます。
コード例:
function createUser(name: string, age: number) { return { name, age, created: new Date() }; } type User = ReturnType<typeof createUser>; // User: { name: string; age: number; created: Date; }
主な特徴:
ReturnTypeをクラスのメソッドで使用できますか?
はい、可能ですが、コンテキストに注意が必要です。メソッドがオブジェクトのプロパティである関数の場合、ReturnType<obj['メソッド']>を使用してください。
コード例:
class MyClass { foo(x: number) { return x * 2; } } type FooReturn = ReturnType<MyClass['foo']>; // タイプエラー! // 以下のようにする必要があります: type FooReturn = ReturnType<(x: number) => number>; // number // またはメソッドを関数として外に出します: const obj = new MyClass(); type FooReturn2 = ReturnType<typeof obj.foo>;
void/neverの型のReturnTypeは何を返しますか?
void型で宣言された関数については、ReturnTypeはvoidを返します。never型についてはneverを返します。
コード例:
function doNothing(): void {} type Result = ReturnType<typeof doNothing>; // void
ReturnTypeは関数のオーバーロードに対応していますか?
いいえ、ReturnTypeは「実装」の返り値の型を抽出し、すべてのオーバーロードの型を抽出するわけではありません。オーバーロードが複数ある場合、実装の記述型が取られます。
コード例:
function func(x: number): number; function func(x: string): string; function func(x: any): any { return x } type RT = ReturnType<typeof func>; // any
プロジェクトで関数の返すオブジェクトの手動型が宣言されます。関数が変更されても型が更新されず、ランタイムエラーが発生します。
利点:
欠点:
関数が返す値を使用する際にReturnTypeに移行します。変更があった場合、型は常に最新の状態になります。
利点:
欠点: