Mapped types
Mapped type преобразует все ключи типа по одному шаблону.
Базовый синтаксис
type Mapped<T> = {
[K in keyof T]: T[K];
};
Проходит по всем ключам T и создаёт новый тип с теми же ключами.
Модификация значений
type Nullable<T> = {
[K in keyof T]: T[K] | null;
};
type User = { name: string; age: number };
type NullableUser = Nullable<User>;
// { name: string | null; age: number | null }
Модификаторы свойств
Знак ? добавляет опциональность:
type MyPartial<T> = {
[K in keyof T]?: T[K];
};
Знак -? убирает:
type MyRequired<T> = {
[K in keyof T]-?: T[K];
};
readonly добавляет:
type MyReadonly<T> = {
readonly [K in keyof T]: T[K];
};
-readonly убирает:
type Mutable<T> = {
-readonly [K in keyof T]: T[K];
};
Переименование ключей
Через as:
type Getters<T> = {
[K in keyof T as `get${Capitalize<string & K>}`]: () => T[K];
};
type User = { name: string; age: number };
type UserGetters = Getters<User>;
// { getName: () => string; getAge: () => number }
Фильтрация ключей
Если as даёт never — ключ удаляется:
type OnlyStrings<T> = {
[K in keyof T as T[K] extends string ? K : never]: T[K];
};
type User = { name: string; age: number; email: string };
type StringFields = OnlyStrings<User>;
// { name: string; email: string }
По union ключей
type PickByKeys<T, K extends keyof T> = {
[P in K]: T[P];
};
type User = { name: string; age: number; email: string };
type NameAndEmail = PickByKeys<User, 'name' | 'email'>;
Это встроенный Pick.
По union значений
type Status = 'idle' | 'loading' | 'success';
type StatusMap = {
[K in Status]: boolean;
};
// { idle: boolean; loading: boolean; success: boolean }
Встроенные mapped types
Все утилиты Partial, Required, Readonly, Pick, Record — это mapped types.
type MyPick<T, K extends keyof T> = {
[P in K]: T[P];
};
type MyRecord<K extends keyof any, V> = {
[P in K]: V;
};
Практика: Optional
type Optional<T, K extends keyof T> = Omit<T, K> & Partial<Pick<T, K>>;
type User = { name: string; age: number; email: string };
type UserWithOptionalEmail = Optional<User, 'email'>;
Практика: DeepPartial
type DeepPartial<T> = {
[K in keyof T]?: T[K] extends object ? DeepPartial<T[K]> : T[K];
};
Практика: DeepReadonly
type DeepReadonly<T> = {
readonly [K in keyof T]: T[K] extends object
? DeepReadonly<T[K]>
: T[K];
};
Практика: FormErrors
type FormErrors<T> = {
[K in keyof T]?: string;
};
type UserForm = { name: string; email: string };
type Errors = FormErrors<UserForm>;
// { name?: string; email?: string }
Практика: Setters
type Setters<T> = {
[K in keyof T as `set${Capitalize<string & K>}`]: (value: T[K]) => void;
};
Итоги
- Mapped types преобразуют все ключи
[K in keyof T]— база?,-?,readonly,-readonly— модификаторыasпереименовывает или фильтрует- Встроенные утилиты — это mapped types
- Полезны для глубоких преобразований