Mapped Types
Способ программно строить типы по форме другого типа. На интервью обязательно: написать Partial, Readonly, Mutable, DeepPartial, NullableProps. Красный флаг — не знать про +/- модификаторы и as-клаузу для key remapping (TS 4.1).
Базовые маппинги и модификаторы readonly / ?:
type Mutable<T> = { -readonly [K in keyof T]: T[K] };type Required2<T> = { [K in keyof T]-?: T[K] };type Optional<T> = { [K in keyof T]+?: T[K] };
interface Conf { readonly host: string; port?: number }type ConfM = Mutable<Conf>; // { host: string; port?: number }type ConfR = Required2<Conf>; // { readonly host: string; port: number }Key remapping через as + filtering по never:
// Префиксуем ключиtype Getters<T> = { [K in keyof T as `get${Capitalize<string & K>}`]: () => T[K]};type UG = Getters<{ id: string; age: number }>;// { getId: () => string; getAge: () => number }
// Фильтр: оставить только функцииtype FunctionKeys<T> = { [K in keyof T as T[K] extends (...a: any[]) => any ? K : never]: T[K]};type Only = FunctionKeys<{ id: string; save(): void; load(): Promise<void> }>;// { save(): void; load(): Promise<void> }Рекурсивный DeepPartial и сохранение массивов:
type DeepPartial<T> = T extends (...a: any[]) => any ? T : T extends readonly (infer U)[] ? readonly DeepPartial<U>[] : T extends object ? { [K in keyof T]?: DeepPartial<T[K]> } : T;
interface User { id: string; profile: { name: string; tags: string[] } }type DP = DeepPartial<User>;// { id?: string; profile?: { name?: string; tags?: readonly string[] } }Итог: Mapped types + as-remapping + модификаторы дают полноценную алгебру преобразований object-типов.