UGA Boxxx

つぶやきの延長のつもりで、知ったこと思ったこと書いてます

【TypeScript】infer

下の型定義をみたときに、extendsはわかるが、inferがよくわかっていなかったので調べた

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

こちらの記事が参考になった

qiita.com

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