TypeScript の型条件分岐: `T extends (...args: infer R) => unknown ? R : never`とは?
この記事の目次
この記事を読んでいるあなたは、TypeScriptの型システムやジェネリクスに興味がある方ですね。ありがとうございます。今回は、TypeScriptで利用することができる条件付き型 (Conditional Type
) と推論 (infer
) について詳しく解説します。
extends
とは?
extends
は、型の条件分岐に使われるキーワードです。TypeScriptの公式ドキュメントには「Conditional Type Constraints」として紹介されています。これは、型に応じて条件を分岐させたい場合に使用します。
https://www.typescriptlang.org/docs/handbook/2/conditional-types.html#conditional-type-constraints
例: 条件分岐による型の制約
例えば、次のようにextends
を使うことで、型T
がstring
型であればstring
型を返し、それ以外であれば元のT
型を返すことができます。
type ConditionalString<T> = T extends string ? string : T; type IdString = ConditionalString<"123456">; // string type IdNumber = ConditionalString<123456>; // 123456
この例では、ConditionalString<"123456">
はstring
型を返しますが、ConditionalString<123456>
はそのままの123456
型を返します。
T extends (...args: infer R) => unknown ? R : never
の意味
T extends (...args: infer R) => unknown ? R : never
という条件は、「型T
が関数型であれば、その引数の型を取得する」という意味になります。
具体的には、この構文では次の2つのことを行っています。
T
が関数型かどうかを確認します。T
が関数型である場合、その引数の型リストR
を推論し、それを結果として返します。関数型でない場合はnever
を返します。
infer
とは?
infer
は「条件付き型の中で型を推論したい場合」に使用するキーワードです。TypeScriptドキュメントには「Inferring Within Conditional Types」として紹介されています。
例: 戻り値の型を取得する型
次の例では、関数の戻り値の型を取得するための条件付き型GetReturnType<T>
を定義しています。
type GetReturnType<T> = T extends (...args: any[]) => infer R ? R : never; const func1 = () => "HelloWorld"; const func2 = (a: number, b: number) => a * b; type TypeFunc1 = GetReturnType<typeof func1>; // string type TypeFunc2 = GetReturnType<typeof func2>; // number type TypeFunc3 = GetReturnType<"123456">; // never
この例では、func1
やfunc2
の戻り値の型がそれぞれ推論され、GetReturnType<typeof func1>
はstring
型、GetReturnType<typeof func2>
はnumber
型になります。
型の意味を失わないための推論
infer
で推論する際に、正確な型を取得するためにはR
を使って型の戻り値を推論することが重要です。次のようにinfer
を省略した場合、すべての戻り値がany
型になってしまい、型システムの意味が薄れてしまいます。
type GetReturnType<T> = T extends (...args: any[]) => any ? any : never; type TypeFunc1 = GetReturnType<typeof func1>; // any type TypeFunc2 = GetReturnType<typeof func2>; // any
実際に使用されるケース
ここで、TypeScriptの型を駆使して、関数の引数の型リストを取得する方法を紹介します。
演習: 引数の型リストを取得する MyParameters
Parameters
のジェネリック型を利用せず、自分で関数の引数の型リストを返す型を実装します。
type MyParameters<T> = T extends (...args: infer P) => unknown ? P : never; const foo = (arg1: string, arg2: number): void => {}; type FunctionParamsType = MyParameters<typeof foo>; // [arg1: string, arg2: number]
この例では、foo
の引数型である[string, number]
が取得できます。引数の型がわかると、関数のテストやエラーハンドリングをより効率的に行うことができます。
まとめ
extends
とinfer
を使うことで、TypeScriptでの型推論と条件分岐がより柔軟に行えるようになります。このような型システムを駆使して、より安全で効率的なコードを書くことが可能になります。TypeScriptを使用する際は、ぜひこれらのテクニックを活用してみてください。