パターンで考えるInfer

に公開

TypeScriptのinferについてしっかり理解できるように使用パターンから考えてみました。
※ 今回はTypeScriptのinferTypes1.tsを参考にしています。

(1) T extends ( infer U )? U : never;

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

type T01 = Unpacked<string[]>;
//  string
type T02 = Unpacked<{a: strng}>;
// never


この場合、 Unpackedの型引数で与えられた型が「Uの配列」を満たすなら「U」が適用され、そうでなければneverが適用されます。

ちなみにですが

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


のような書き方も可能です。

(2) T extends (...args: any[] ) => infer U ? U : never;

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

type T03 = Unpacked<() => string>;
// string
type T04 = Unpacked<() => number>;
// number

type T05 = Unpacked<number>;
// never


こちらは「Tが返り値がUの型を返す関数の型」を満たす場合は「U」を適用、そうでなければneverを適用となります。

より具体的な関数で見てみます

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

const sum = (a: number, b: number): number => {
  return a + b;
};

const sum2 = (a: number, b: number, c: number): number => {
  return a + b + c;
};

type T06 = Unpacked<typeof sum>;
// number

type T07 = Unpacked<typeof sum>;
// number
// (...args: any[])とすることで引数が3つに増えても対応可能

このようにsumというnumberを返す関数に使用すると返り値の型を取得することが可能になります。

(3) T extends Promise<infer U>? U : never;

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

type T08 = Unpacked<Promise<string>>;
// string;


これはTの型が「Promise<U>」になる場合「U」を適用する、そうでなければneverが適用となります。

まとめ

3つの例でのわかったinferの特徴としては
①Conditional Typeで使用される
②条件式で使用されている型を再利用したい時(抽出したい時)に使用できる
③Conditional Type内で使用されるinfer UのUはこの時点では未確定
という特徴があるということがわかりました。