マップドタイプは、他のタイプのすべてのプロパティを動的に変換(名前変更、修正)することで構築されるタイプです。構文はin構文に基づいています:
type Readonly<T> = { readonly [K in keyof T]: T[K]; } type User = { name: string; age: number; } const u: Readonly<User> = { name: 'Eve', age: 22 }; u.name = 'Bob'; // エラー:nameは読み取り専用です
ニュアンス:
-? または +?を使います。すべての修飾子を含んだ例:
type PartialMutable<T> = { -readonly [K in keyof T]?: T[K]; };
「optional修飾子を持つマップドタイプを適用した場合、それは第一段階のプロパティのみに影響しますか、それとも入れ子のオブジェクトにも影響しますか?」
回答: いいえ、optional ?を持つマップドタイプは、第一段階のプロパティにのみ影響します。入れ子のオブジェクトは別途変換する必要があり、しばしば再帰や追加のマップドタイプを用います。
例:
type DeepPartial<T> = { [K in keyof T]?: T[K] extends object ? DeepPartial<T[K]> : T[K]; };
逸話
あるプロジェクトで時間を節約するために、深いフォームのボリューム用に標準のPartial<T>を使用しました。しかし、第二層と第三層のフィールドはオプショナルにならず、入れ子のキーが存在しないときに予期しないランタイムエラーに繋がりました。
逸話
子オブジェクトだけのreadonlyプロパティを削除しようとし、上位レベルにのみマップドタイプを適用しました:
type Mutable<T> = { -readonly [K in keyof T]: T[K] }
その結果、{ readonly foo: { readonly bar: number } } の型のフィールドは入れ子の中で変更されず、チームを混乱させ、メンテナンスが複雑になりました。
逸話
複雑なデータモデルで、複数のユーティリティタイプ(例えば、Readonly & Partial)を交差させるために入れ子のマップドタイプを適用しました。これらの組み合わせの順序が間違っていたため、型の互換性に関する予期しない競合が発生し、コンパイラは混乱したエラーメッセージを出し始めました。