ПрограммированиеSenior TypeScript разработчик

Что такое Mapped Types в TypeScript, как с их помощью создавать гибкие обобщённые типы? Раскройте подробно нюансы их использования и потенциальные ловушки.

Проходите собеседования с ИИ помощником Hintsage

Ответ.

Mapped Types — это типы, которые строятся динамически путём преобразования (переименования, модификации) всех свойств другого типа. Синтаксис основан на конструкции 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), убирать или добавлять их через ключевое слово -? или +?.
  • Могут применяться вложенно, комбинироваться с conditional types, utility types и generics.
  • Не всегда легко трассируются ошибки на этапе разработки, особенно при сложной вложенной модификации иерархий типов.

Пример со всеми модификаторами:

type PartialMutable<T> = { -readonly [K in keyof T]?: T[K]; };

Вопрос с подвохом.

«Применив mapped type с модификатором optional, влияет ли это только на свойства первого уровня или на вложенные объекты тоже?»

Ответ: Нет, mapped type с optional ? влияет ТОЛЬКО на свойства первого уровня. Вложенные объекты нужно преобразовывать отдельно, часто с помощью рекурсии или дополнительных mapped types.

Пример:

type DeepPartial<T> = { [K in keyof T]?: T[K] extends object ? DeepPartial<T[K]> : T[K]; };

Примеры реальных ошибок из-за незнания тонкостей темы.


История

В одном проекте на экономии времени применяли стандартный Partial<T> для deep-объема формы. Однако, поля второго и третьего уровня не стали опциональными, что привело к неожиданным ошибкам на runtime при отсутствии вложенных ключей.


История

Была попытка убрать readonly свойства только в дочерних объектах, применив mapped type только на верхний уровень:

type Mutable<T> = { -readonly [K in keyof T]: T[K] }

В результате, поля типа { readonly foo: { readonly bar: number } } оставались неизменными во вложенности, что запутало команду и усложнило поддержку.


История

В сложной модели данных применили вложенные mapped types для пересечения нескольких utility types (например, Readonly & Partial). Из-за неправильного порядка их композиции возникли неожиданные конфликты по совместимости типов, и компилятор начал выводить запутанные сообщения об ошибках.