見出し画像

TypeScript の infer キーワードを深掘りする

TypeScript の `infer` キーワードは、条件型の中で使用され、型を推論するために使われます。これは複雑な型を扱う際に非常に便利で、型を抽出したり変換したりするのに役立ちます。

基本的な使い方

`infer` キーワードは条件型の中でのみ使用でき、通常はジェネリクスと `extends` キーワードと組み合わせて使われます。その基本的な構文は以下のようになります。

type Moment<T> = T extends infer U ? U : never;

ここで `T extends infer U` は、型 `T` を推論し、それを `U` に代入しようとすることを意味します。型の推論が成功した場合、`U` が推論された型となります。

次に、さまざまな型を推論する例を見てみましょう。

type Moment<T> = T extends infer U ? U : never;

type StringType = Moment<string>; // string
type NumberType = Moment<number>; // number
type UnionType = Moment<string | number>; // string | number

interface User {
  name: string;
  age: number;
}

type UserType = Moment<User>; // User

これらの例では、`Moment<T>` は単に `T` の型を返すだけで、変換や処理は行いません。これは条件型と型推論の基本的な使い方を示すための例です。

よくある使用例

関数の戻り値の型を抽出する

関数の型がある場合、その戻り値の型を取得したいとします。その場合、次のように `infer` を使えます。

type GetReturnType<T> = T extends (...args: any[]) => infer R ? R : never;

type ExampleFunction = (x: number, y: string) => boolean;
type ReturnTypeOfExampleFunction = GetReturnType<ExampleFunction>; // boolean

上記のコードでは、以下のように動作します。

  1. `T extends (...args: any[]) => infer R`: `T` が関数型であるかをチェックします。`(...args: any[])` は、その関数が任意の数の引数を受け取ることを示します。

  2. `infer R`: `T` が関数型である場合、関数の戻り値の型を `R` として推論します。

  3. `? R : never`: `T` が関数型なら `R` を返し、そうでなければ `never` を返します。

配列要素の型を抽出する

`infer` を使用して配列の要素の型を取得することもできます。

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

type Moment = string[];
type Example1Array = Array<string>;

type ElementTypeOfExampleArray = GetArrayElementType<Moment>; // string
type ElementTypeOfExample1Array = GetArrayElementType<Example1Array>; // string

ここでは、`T extends (infer U)[]` を使って、配列の要素型 `U` を推論しています。例えば、`T` が `string[]` の場合、`U` は `string` となります。

string[] extends (infer U)[]

重要なのは、`infer` は条件型の `extends` 句の中でのみ使用可能であり、`infer` で宣言された型変数は `true` 分岐の中でのみ有効である点です。

応用例

`Promise` の値の型を抽出する

`Promise` 型がある場合、その解決後の値の型を取得できます。

type GetPromiseValueType<T> = T extends Promise<infer U> ? U : never;

// 使用例
type ExamplePromise = Promise<number>;
type ValueTypeOfExamplePromise = GetPromiseValueType<ExamplePromise>; // number

このコードでは、次のように動作します。

  1. `T extends Promise<infer U>`: `T` が `Promise` 型かどうかをチェックします。

  2. `infer U`: `T` が `Promise` 型である場合、その値の型を `U` として推論します。

  3. `? U : never`: `T` が `Promise` 型なら `U` を返し、そうでなければ `never` を返します。

関数のパラメーターの型を取得する

関数のパラメーター型を取得したい場合、`infer` を使用できます。

type GetParameters<T> = T extends (...args: infer P) => any ? P : never;

type ExampleFunction = (a: number, b: string) => void;
type Params = GetParameters<ExampleFunction>; // [number, string]

ここでは、`T extends (...args: infer P) => any` によって `T` が関数型かを判定し、`infer P` で関数のパラメーターの型を推論しています。

コンストラクターのパラメーターの型を取得する

クラスのコンストラクターのパラメーター型を取得することもできます。

type ConstructorParameters<T> = T extends new (...args: infer P) => any ? P : never;

class ExampleClass {
  constructor(public a: number, public b: string) {}
}

type Params = ConstructorParameters<typeof ExampleClass>; // [number, string]

条件型での複雑な推論

条件型の中で、より複雑な推論を行うことも可能です。

type IsArray<T> = T extends (infer U)[] ? U : never;
type IsFunction<T> = T extends (...args: any[]) => infer R ? R : never;

type ExtractType<T> = T extends any[]
  ? IsArray<T>
  : T extends (...args: any[]) => any
  ? IsFunction<T>
  : T;

// 使用例
type ArrayType = ExtractType<string[]>; // string
type FunctionReturnType = ExtractType<() => number>; // number
type DefaultType = ExtractType<boolean>; // boolean

ここでの処理は以下の通りです。

type ExtractType<T> = T extends any[]
  ? IsArray<T>
  : T extends (...args: any[]) => any
  ? IsFunction<T>
  : T;
  1. `T extends any[] ? IsArray<T>`: `T` が配列型なら、要素の型を返します。

  2. `T extends (...args: any[]) => any ? IsFunction<T>`: `T` が関数型なら、戻り値の型を返します。

  3. `T`: それ以外の場合は `T` 自体を返します。

まとめ

`infer` キーワードは条件型の中で使用され、他の型から新しい型変数を推論するために用いられます。これにより、型チェックの際に特定の部分型やプロパティを抽出して使用できるため、型システムの表現力と柔軟性が向上します。簡単に言うと、`infer` を使うことで、複雑な型の中から必要な部分だけを自動で抽出することができます。


私たちはLeapcell、Node.jsプロジェクトのホスティングの最適解です。

Leapcellは、Webホスティング、非同期タスク、Redis向けの次世代サーバーレスプラットフォームです:

複数言語サポート

  • Node.js、Python、Go、Rustで開発できます。

無制限のプロジェクトデプロイ

  • 使用量に応じて料金を支払い、リクエストがなければ料金は発生しません。

比類のないコスト効率

  • 使用量に応じた支払い、アイドル時間は課金されません。

  • 例: $25で6.94Mリクエスト、平均応答時間60ms。

洗練された開発者体験

  • 直感的なUIで簡単に設定できます。

  • 完全自動化されたCI/CDパイプラインとGitOps統合。

  • 実行可能なインサイトのためのリアルタイムのメトリクスとログ。

簡単なスケーラビリティと高パフォーマンス

  • 高い同時実行性を容易に処理するためのオートスケーリング。

  • ゼロ運用オーバーヘッド — 構築に集中できます。

ドキュメントで詳細を確認!

Xでフォローする:@LeapcellHQ


ブログでこの記事を読む

いいなと思ったら応援しよう!