Type Challenges 攻略ヒント集

仕事仲間から面白いサイトを教えてもらった。お題に沿ったTypeScriptの型定義を作る問題集のようなサイトだ。

tsch.js.org

TypeScriptの型定義について知らなかったことが多々あったため、攻略テクニックとして以下にまとめる。

配列型、タプル型をマップする

[string, number] 型から [string[], number[]] 型を得る型を定義したいとする。

// object を定義しそうな見た目だが、結果は配列型 or タプル型になる
type ArraysOfArray<T extends any[]> = {
  [K in keyof T]: T[K][]
};

ユニオン型をマップする

string | number 型から string[] | number[] 型を得る型を定義したいとする。

// NG例 ... Tはユニオン型のまま扱われるため (string | number)[] になる
type ArraysOfUnion<T> = T[];

// OK例
type ArraysOfUnion<T> = T extends T ? T[] : never;
type ArraysOfUnion<T extends keyof any> = { [K in T]: K[] }[T];

参考: 296 - Permutation (with explanations) · Issue #614 · type-challenges/type-challenges · GitHub

プロパティを値でフィルタする

値が string 型であるプロパティのみを抽出したオブジェクトの型を得る型を定義したいとする。

type ExtractStringProperty<T> = {
  [K in keyof T as T[K] extends string ? K : never]: T[K]
};

参考: TypeScript: Documentation - Mapped Types

インターセクション型を集約する

インターセクション型を用いて { a: number; b: string } を得る型を定義したいとする。

// NG例 ... 実用上は正解だがインターセクション型のままでは不正解となる
type AB = { a: number } & { b: string };

// OK例
type AB = Omit<{ a: number } & { b: string }, never>;

型パラメータが never であるかどうかを判定する

// NG例 ... T が never の場合、結果は true ではなく never になる
type IsNever<T> = T extends never ? true : false;

// OK例
type IsNever<T> = [T] extends [never] ? true : false;
type IsNever<T> = T[] extends never[] ? true : false;