TypeScript infer キーワード

infer は TypeScript の条件型におけるキーワードで、型から新しい型を推論するために使用されます。

これにより、ジェネリック条件型で型を抽出・導出でき、強力な型操作を実現できます。


SVG 図:infer の動作原理 背景 タイトル infer キーワードの動作原理 元の型 元の型 type Promise<T> = { value: T } 矢印 infer による推論 推論結果 抽出された型 type ValueOf<T> = T extends Promise<infer V> ? V : never 例 例 ValueOf<Promise<string>> → string 下半分:応用シナリオ infer の一般的な応用シナリオ シナリオ1 Promise の値の型を抽出 シナリオ2 配列要素の型を抽出 シナリオ3 関数の戻り値の型を抽出 矢印マーカー

なぜ infer キーワードが必要なのか

ジェネリックプログラミングでは、複雑な型から部分的な型を抽出する必要がよくあります。

例えば、Promise<string>から抽出するstring、Array<User>から抽出するUser。

infer キーワードは、この種の型抽出をエレガントかつ型安全にします。条件型で型変数を宣言し、true ブランチで使用できます。

概念の説明:infer は「infer」の略で、「推論」を意味します。条件型の extends 句でのみ使用でき、推論待ちの型変数を宣言するために使用します。


基本的な使い方

条件型で infer を使用して型を推論します。

実例

// Promise から値の型を抽出する
// T が Promise<V> の場合は V を返し、それ以外の場合は never を返す
type ValueOf<T> = T extends Promise<infer V> ? V : never;

// テスト
type Str = ValueOf<Promise<string>>;  // string
type Num = ValueOf<Promise<number>>; // number
type NotPrm = ValueOf<string>;        // never

// 使用例
var promise: Promise<string> = Promise.resolve("hello");
var value: ValueOf<typeof promise> = "world";

console.log("文字列抽出: " + (typeof value === "string" ? "成功" : "失敗"));

実行結果:

文字列抽出: 成功

説明:infer V型変数 V を宣言すると、TypeScript が自動的に V の型を推論します。


配列要素の型を抽出する

配列型から要素の型を抽出します。

実例

// 配列の要素の型を抽出する
// T が配列 V[] の場合は V を返す
type ArrayElement<T> = T extends (infer V)[] ? V : never;

// テスト
type User = { name: string };
type Users = User[];

type E1 = ArrayElement<string[]>;   // string
type E2 = ArrayElement<Users>;       // User
type E3 = ArrayElement<number>;       // never(配列ではない)

// 使用例
var users: Users = [{ name: "Alice" }];
var element: ArrayElement<Users> = users[0];

console.log("要素の型: " + element.name);

応用:このパターンは、ユニオン型から特定の型の要素を抽出するためによく使用されます。


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

関数型から戻り値の型を抽出します。

実例

// 関数の戻り値の型を抽出する
type ReturnType<T> = T extends (...args: any[]) => infer R ? R : any;

// テスト
function getData() {
    return { id: 1, name: "Alice" };
}
function fetchUser(id: number): Promise<User> {
    return Promise.resolve({ id, name: "Bob" });
}

type R1 = ReturnType<typeof getData>;  // { id: number; name: string }
type R2 = ReturnType<typeof fetchUser>; // Promise<User>

// 使用例
var result: R1 = { id: 1, name: "Test" };
console.log("戻り値の型: " + JSON.stringify(result));

組み込み型:TypeScript 組み込みのReturnType<T>ユーティリティ型は infer を使用して実装されています。


関数のパラメータの型を抽出する

関数型からパラメータの型を抽出します。

< h2 class="example">実例
// 関数の最初のパラメータの型を抽出する
type FirstParameter<T> = T extends (first: infer P, ...rest: any[]) => any ? P : never;

// テスト
function createUser(name: string, age: number): User {
    return { id: 1, name };
}
function logMessage(msg: string): void {
    console.log(msg);
}

type P1 = FirstParameter<typeof createUser>; // string
type P2 = FirstParameter<typeof logMessage>;  // string
type P3 = FirstParameter<() => void>;          // never

// 使用例
var nameParam: P1 = "Alice";
console.log("パラメータの型: " + nameParam);

注意:を使用して、...rest: any[]任意の数の残りのパラメータにマッチさせることができます。


複数の infer

1つの条件型で複数の infer を使用します。

実例

// タプルの最初の2つの要素の型を抽出する
type FirstTwo<T> = T extends [infer A, infer B, ...rest: any[]] ? [A, B] : never;

// テスト
type Tuple = [string, number, boolean];
type FirstTwoTypes = FirstTwo<Tuple>; // [string, number]

// オブジェクトのプロパティを抽出する
type ObjectValue<T> = T extends { value: infer V } ? V : never;

type WithValue = { value: string; name: string };
type ExtractedValue = ObjectValue<WithValue>; // string

// 使用例
var tupleResult: FirstTwoTypes = ["hello", 123];
console.log("タプル: " + JSON.stringify(tupleResult));

タプル:を使用して...rest: any[]タプルの残りの要素にマッチさせることができます。


再帰型で infer を使用する

再帰型ツールで infer を使用して、複雑な型操作を実現します。

実例

// ディープ読み取り専用型 - infer を再帰的に適用
type DeepReadonly<T> = T extends Function
    ? T
    : T extends object
        ? { readonly [P in keyof T]: DeepReadonly<T[P]> }
        : T;

// テスト
interface User {
    name: string;
    address: {
        city: string;
        zip: string;
    };
}

type ReadonlyUser = DeepReadonly<User>;

// Promise 型をフラット化する
type FlattenPromise<T> = T extends Promise<infer U>
    ? U extends Promise<any>
        ? FlattenPromise<U>
        : U
    : T;

type Nested = Promise<Promise<string>>;
type Flat = FlattenPromise<Nested>; // string

// 使用例
var user: ReadonlyUser = {
    name: "Alice",
    address: { city: "Beijing", zip: "100000" }
};
// user.name = "Bob"; // エラー:読み取り専用
console.log("ディープ読み取り専用: " + user.name);

再帰 infer:再帰型で infer を使用すると、ディープ読み取り専用、ディープオプショナルなどの深い型変換を実現できます。


注意事項

  • extends の右側でのみ使用可能:infer は条件型の extends 句でのみ使用できます
  • 型変数の宣言:を使用してinfer V推論待ちの型変数を宣言します
  • 複数使用可能:1つの条件型で複数の infer を使用できます
  • 推論に失敗した場合は never を返す:推論に失敗した場合、条件型は never を返します

ベストプラクティス:infer はカスタムユーティリティ型を実装するための核心であり、これを習得することで強力な型操作を実現できます。


まとめ

infer は TypeScript の型システムにおける最も強力な機能の1つです。

  • 型の抽出:複雑な型から部分的な型を抽出する
  • 条件推論:条件型で型を推論する
  • 関数関連:関数のパラメータ、戻り値の型を抽出する
  • 再帰ツール:深い型変換を実現する

アドバイス:infer を深く理解することで、より高度な型ツールを作成でき、型の安全性と開発効率を向上させることができます。

その他の拡張