Software Design (ソフトウェアデザイン) 2024年05月号を読んでMapped Typesを知った
Mapped Typesは「ある型から条件を満たした別の型に変換する」という型変換ができる機能
本では使い方の例として、2つのモデル(User, DraftUser)で説明されている
UserとDraftUserの違いはidとcreatedAtがオプショナルかどうかの違いがある
type User = { id: string; name: string; createdAt: number; } type DraftUser = { id?: string; name: string; createdAt?: number; }
UserとDraftUserが対応関係にある場合、独立して定義していると変更漏れが発生する恐れがある
なのでMapped Typesを使ってOptionalユーティリティを作りこれを以下のように書き直すことができる
type Optional<T extends {}, K extends keyof T> = Omit<T, K> & { [k in K]?: T[k] | undefined; }; type DraftUser = Optional<User, "id" | "createdAt">;
やっていることは
- 型引数に対してextendsで推論しやすい型制約を設定する
- keyof T に対してループ展開([k in K]はループ展開と考えてよい)
- T[k]のサブタイプを確認し、プロパティの型を条件型で表現する
本にあったTIPSとして、このままだとIDEでの型情報が読みづらいのでIdentity<T>
ユーティリティも作ると読みやすくなるの知らなかった
type Identify<T> = T extends infer U ? { [K in keyof U]: K } : never; type DraftUser2 = Identify<Optional<User, "id" | "createdAt">>