🧐

【備忘録】TypeScript - 型推論

2024/08/22に公開

Extractをジェネリックな関数で使った時の型推論のプロセス

背景

ジェネリック型の引数を持つ関数でExtractを使用した時のTypeScriptの型推論のプロセスを理解するのに苦労した。
特に以下のgetValues関数のUがどのように型推論されるのか理解が難しかったため、型推論プロセスを備忘録に残しておく。

例に使うgetValues関数について

第一引数にobject型のobj、第二引数にobjが持つキーの一部を格納したstring型の配列keysを持ち、keysの要素に一致するキーの値を格納したU型の配列を返す。

function getValues<T, U>(obj: T, keys: Extract<keyof T, U>[]): U[] {
  return keys.map(key => obj[key]);
}

Uの推論プロセス

sample.ts
function getValues<T, U>(obj: T, keys: Extract<keyof T, U>[]): U[] {
  return keys.map(key => obj[key]);
};

const user = {
  id: 1,
  name: "hoge",
  isAdmin: true
};

const values = getValues(user, ["name", "isAdmin"]);

上記のプログラムでは、以下のような推論プロセスでUが推論される。

  1. Tobjの型として確定する
    objuserが渡されると、Tは{id: number, name: string, isAdmin: boolean}のオブジェクトリテラル型に基づいて確定される。
  2. Extract<keyof T, U>の適用
    Extract<keyof T, U>が適用されると、keyof T("id" | "name" | "isAdmin")の中からUに該当するプロパティ名(["name", "isAdmin"])を抽出するため、keysの型がTのプロパティ名と一致することが保証される。
  3. Uの初期推論
    この時点でUは"name" | "isAdmin"の文字列リテラルのユニオン型として推論される可能性がある。
  4. Uの最終推論
    keys.map(key => obj[key]によって、obj[key]の型がU[]の型に割り当てられることになる。つまりUは["hoge", true]の配列の要素の型、string | booleanとして最終的に推論される。

Discussion