ProgrammingシニアTypeScript開発者

TypeScriptにおけるマップドタイプとは何ですか?それを使ってどのように柔軟なジェネリックタイプを作成するのか、使用のニュアンスや潜在的な落とし穴について詳しく説明してください。

Hintsage AIアシスタントで面接を突破

回答。

マップドタイプは、他のタイプのすべてのプロパティを動的に変換(名前変更、修正)することで構築されるタイプです。構文は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は読み取り専用です

ニュアンス:

  • 修飾子(readonly、optional)を変更したり、削除または追加することができます。キーワード -? または +?を使います。
  • 入れ子に適用したり、条件付きタイプ、ユーティリティタイプ、ジェネリックと組み合わせて使うことができます。
  • 複雑な入れ子の修正や型階層の複雑さにより、開発段階でエラーを追跡するのが容易ではないことがあります。

すべての修飾子を含んだ例:

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)を交差させるために入れ子のマップドタイプを適用しました。これらの組み合わせの順序が間違っていたため、型の互換性に関する予期しない競合が発生し、コンパイラは混乱したエラーメッセージを出し始めました。