下の型定義をみたときに、extendsはわかるが、inferがよくわかっていなかったので調べた
type Unpacked<T> = T extends (infer U)[] ? U : T extends (...args: any[]) => infer U ? U : T extends Promise<infer U> ? U : T;
こちらの記事が参考になった
infer
Conditional Types構文の中のみで利用できるシグネチャ
Conditional Types の構文の中で型をキャプチャする機能
部分的な型抽出ができる
「Type Inference in Conditional Types」と呼ばれる
例えば、違う型のcodeプロパティをもつ2つのclassがあったとき
class Book { constructor( public code: number, public name: string, ) {} } class Ticket { constructor( public code: string, public name: string, ) {} }
code プロパティを U 型としてキャプチャすることができる
type Code<T> = T extends { code: infer U } ? U : never; // Code<Book> = T extends { code: infer number } ? number : never --> number // Code<Ticket> = T extends { code: infer string } ? string : never --> string
これを使うと例えば、BookとTicket を抽象的に扱う型を宣言しておき、BookでもTicketでもどちらでも検索可能にすることができる
type Item<T> = { code: Code<T> }; function filter<U>(list: Item<U>[], code: Code<U>): Item<U>[] { return list .filter(item => item.code === code) ; }
これにより、型を間違えた場合にコンパイルエラーがでる
// Error: Argument of type '"2"' is not assignable to parameter of type 'number'. console.info(filter<Book>(orderedBook, '2'));
読めなかった下の部分は
(...args: any[]) => infer U
「関数型かつ戻り型がある」型を表していて
こちらの部分は
T extends Promise<infer U>
Promiseの値の型を表していることがわかった
参考
https://www.typescriptlang.org/docs/handbook/advanced-types.html#type-inference-in-conditional-types