導入
「この型から、配列の要素の型だけを取り出したい」「関数の引数の型だけを取り出したい」といった、型そのものに対する条件分岐をしたいことがあります。条件型(extends ? :)と infer を使うと、型の中から別の型を取り出せます。ライブラリの型定義を読むときによく登場します。
説明
条件型は T extends U ? X : Y の形で、「T が U に当てはまるなら X、そうでなければ Y」という型を作ります。
type IsString<T> = T extends string ? "yes" : "no";
type A = IsString<string>; // "yes"
type B = IsString<number>; // "no"
let a: A = "yes";
let b: B = "no";
console.log(a, b);
infer を使うと、条件型の中で「型の一部を変数のように取り出す」ことができます。たとえば配列の要素の型を取り出す型はこう書けます。
type ElementType<T> = T extends (infer U)[] ? U : never;
type NumType = ElementType<number[]>; // number
let n: NumType = 42;
console.log(n);
infer U は「T が配列なら、その要素の型を U として受け取る」という意味です。
やってみよう
type StrType = ElementType<string[]>; を追加し、let s: StrType = "文字列"; console.log(s); を試してみましょう。
演習
type FirstArg<T> = T extends (arg: infer A) => unknown ? A : never; という条件型を作ってください。function double(x: number): number { return x * 2; } があるとき、FirstArg<typeof double> で引数の型 ArgType を取り出し、ArgType 型の変数 v に 21 を入れて console.log(v * 2) で結果を表示してください。
ヒント1を見る
type FirstArg<T> = T extends (arg: infer A) => unknown ? A : never; と書きます。
ヒント2を見る
type ArgType = FirstArg<typeof double>; let v: ArgType = 21; console.log(v * 2);