Что такое partial types (Partial, Pick, Omit) и как их комбинировать в TypeScript?

MiddleTypeScript · Frontend·Обновлено 4 сентября 2026
Коротко
Partial, Pick и Omit — встроенные utility types, которые трансформируют существующий тип: Partial делает все поля опциональными, Pick выбирает подмножество полей, а Omit исключает указанные поля. Их можно комбинировать друг с другом и с другими utility types для построения новых типов без дублирования кода.

Зачем нужны utility types

Utility types — это generic-типы, встроенные в стандартную библиотеку TypeScript (lib.es5.d.ts), которые берут существующий тип и производят из него новый. Они позволяют не дублировать описания форм данных, а выводить их друг из друга.

Partial<T>

Делает все свойства типа T опциональными (?). Полезен, когда нужно описать частичное обновление объекта, например тело PATCH-запроса.

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

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

updateUser(1, { name: 'Anton' });

Под капотом это mapped type:

type Partial<T> = {
  [P in keyof T]?: T[P];
};

Pick<T, K>

Выбирает из типа T только перечисленные в K ключи (K extends keyof T). Используется, когда нужен «срез» большого типа для конкретного компонента или функции.

type UserPreview = Pick<User, 'id' | 'name'>;
// { id: number; name: string }

Реализация:

type Pick<T, K extends keyof T> = {
  [P in K]: T[P];
};

Omit<T, K>

Противоположность Pick — исключает перечисленные ключи, оставляя всё остальное. Реализован через комбинацию Pick и Exclude:

type Omit<T, K extends keyof any> = Pick<T, Exclude<keyof T, K>>;

Пример: убрать служебные поля перед отправкой формы.

type UserFormValues = Omit<User, 'id'>;
// { name: string; email: string }

Важный нюанс

Omit менее строгий, чем Pick: ключи K не обязаны существовать в T (K extends keyof any, то есть string | number | symbol). Это удобно для generic-кода, но может маскировать опечатки в имени поля — TypeScript не подскажет ошибку, если исключаемого поля вообще не было в типе.

Комбинирование

Utility types компонуются как обычные функции — результат одного можно передать в другой.

// Опциональные только выбранные поля, остальные обязательны
type PartialUserInfo = Partial<Pick<User, 'name' | 'email'>>;

// DTO для обновления: без id, всё остальное опционально
type UpdateUserDto = Partial<Omit<User, 'id'>>;

// Собственная переиспользуемая обёртка
type PartialBy<T, K extends keyof T> = Omit<T, K> & Partial<Pick<T, K>>;

type DraftUser = PartialBy<User, 'email'>;
// id и name обязательны, email — опционален

Типичные практические паттерны

  • Pick<T, K> — для пропсов компонента, которым нужна только часть большой модели.
  • Omit<T, 'id' | 'createdAt'> — для форм создания сущности, где служебные поля генерируются сервером.
  • Partial<T> — для update-функций, reducer-ов, объектов настроек со значениями по умолчанию.
  • Комбинация Omit + Partial — классический паттерн DTO для PATCH-эндпоинтов.

Когда что использовать

Если нужно оставить только несколько полей — Pick. Если полей много, а исключить нужно одно-два — Omit. Если структура остаётся той же, но часть полей должна стать необязательной — Partial, при точечной опциональности — комбинация Omit + Partial<Pick<...>>.

Что хочет услышать интервьюер

Кандидат объясняет назначение каждого utility type своими словами, а не просто заучивает названия

Понимание, что это mapped types, построенные на keyof и index access types

Умение показать, как Omit реализован через Pick и Exclude

Практический пример комбинирования (например Partial<Omit<T, 'id'>> для DTO обновления)

Осознание нюанса, что Omit не проверяет принадлежность ключа типу, в отличие от Pick

Пример: Базовое использование Partial, Pick и Omit

interface Product {
  id: number;
  title: string;
  price: number;
  description: string;
}

// Все поля опциональны
type ProductPatch = Partial<Product>;

// Только id и title
type ProductPreview = Pick<Product, 'id' | 'title'>;

// Всё, кроме id
type ProductInput = Omit<Product, 'id'>;

Пример: Комбинирование для DTO обновления

// Обновление сущности: id обязателен, остальные поля опциональны
type UpdateProductDto = { id: number } & Partial<Omit<Product, 'id'>>;

function updateProduct(dto: UpdateProductDto) {
  // dto.id всегда есть, остальные поля — по необходимости
}

updateProduct({ id: 1, price: 999 });

Пример: Собственный обобщённый helper PartialBy

// Делает опциональными только перечисленные поля K, остальные остаются как есть
type PartialBy<T, K extends keyof T> = Omit<T, K> & Partial<Pick<T, K>>;

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

// id и name обязательны, email опционален
type DraftUser = PartialBy<User, 'email'>;

const draft: DraftUser = { id: 1, name: 'Anton' };

Типичные ошибки

Путают Pick и Omit местами или не могут объяснить разницу без примера

Не знают, что Partial делает поля опциональными только на первом уровне (не рекурсивно для вложенных объектов)

Не понимают, что K в Pick ограничен keyof T, а в Omit — нет, и не видят из-за этого риск опечатки

Пытаются комбинировать типы через пересечение (&) там, где нужно вложенное применение (Partial<Pick<...>>), и получают неверный результат

Не умеют написать собственный производный utility type вроде PartialBy при просьбе обобщить паттерн

Лучшие курсы по теме

изображение курса

TypeScript с нуля

Антон Ларичев
AI-тренажерыAI-тренажеры
Практика в студииПрактика в студии
Гарантия
Бонусы
иконка звёздочки рейтинга4.8
3 999 ₽ 6 990 ₽
Подробнее
изображение курса

Feature-Sliced Design

Антон Ларичев
AI-тренажерыAI-тренажеры
Практика в студииПрактика в студии
Гарантия
Бонусы
иконка звёздочки рейтинга4.6
3 999 ₽ 6 990 ₽
Подробнее
изображение курса

Next.js - с нуля

Антон Ларичев
AI-тренажерыAI-тренажеры
Практика в студииПрактика в студии
Гарантия
Бонусы
иконка звёздочки рейтинга4.7
3 999 ₽ 6 990 ₽
Подробнее