Утилитарные типы: Partial, Pick, Omit, Record

Готовые инструменты, которые конструируют новые типы из уже существующих.

Утилитарные типы — встроенные дженерики TypeScript, которые преобразуют один тип в другой: делают свойства необязательными, выбирают часть полей и т. д.

Зачем они нужны

Часто нужен тип, «похожий» на существующий, но с отличиями: те же поля, но все необязательные; только часть полей; всё без одного поля. Переписывать тип вручную — дублирование, которое легко рассинхронизировать. Утилитарные типы строят такие варианты автоматически из исходного типа.

Все примеры строятся на базовом интерфейсе:

interface User {
  id: number;
  name: string;
  email: string;
  age: number;
}

Partial — все поля необязательны

Partial<T> делает все свойства опциональными. Идеально для функций обновления, где меняют только часть полей:

function updateUser(id: number, changes: Partial<User>) {
  // changes может содержать любое подмножество полей User
}

updateUser(1, { age: 31 });            // ок — только возраст
updateUser(1, { name: "Боря", age: 40 }); // ок — несколько полей

Без Partial пришлось бы передавать целиком весь объект User или вручную писать тип со всеми ?.

Required — все поля обязательны

Обратная операция: Required<T> делает все свойства обязательными, убирая ?. Полезно, когда нужно гарантировать заполненность всех полей.

Pick — выбрать часть полей

Pick<T, Keys> создаёт тип из перечисленных свойств. Удобно для «облегчённых» представлений:

type UserPreview = Pick<User, "id" | "name">;
// эквивалентно { id: number; name: string }

const preview: UserPreview = { id: 1, name: "Аня" }; // email и age не нужны

Omit — всё, кроме указанного

Omit<T, Keys> — наоборот, берёт все поля, кроме перечисленных. Классика — тип для создания записи без id (его выдаёт база):

type NewUser = Omit<User, "id">;
// { name: string; email: string; age: number }

function createUser(data: NewUser) { /* ... id присвоит база */ }

Record — типизированный словарь

Record<Keys, Value> создаёт тип объекта с заданными ключами и типом значений — компактная альтернатива индексной сигнатуре:

type Roles = Record<string, boolean>;
// эквивалентно { [key: string]: boolean }

const access: Record<"read" | "write", boolean> = {
  read: true,
  write: false,
}; // ключи ограничены литералами — пропустить один нельзя

Памятка

УтилитаЧто делает
Partial<T>все свойства необязательны
Required<T>все свойства обязательны
Pick<T, K>только выбранные поля
Omit<T, K>все поля, кроме указанных
Record<K, V>объект с ключами K и значениями V

Главная польза — единый источник правды. Меняете базовый User — все производные типы (Partial<User>, Omit<User, "id">) обновляются сами, без ручной правки.

Итог

  • Partial/Required переключают обязательность всех свойств.
  • Pick и Omit выбирают или исключают поля исходного типа.
  • Record<K, V> описывает словарь; все утилиты автоматически следуют за изменениями базового типа.
Проверьте себя
1. Что делает Partial<User>?
AУдаляет все поля из User
BДелает все свойства User необязательными
CДелает все свойства User обязательными
DОставляет только поле id
2. Какой утилитой создать тип со всеми полями User, кроме id?
APick<User, "id">
BOmit<User, "id">
CPartial<User>
DRecord<User, "id">
3. В чём главное преимущество утилитарных типов перед ручным переписыванием?
AОни работают быстрее в рантайме
BПроизводные типы автоматически обновляются при изменении базового типа
CОни шифруют данные
DОни уменьшают размер итогового JavaScript
Поддержать проект