Что такое partial types (Partial, Pick, Omit) и как их комбинировать в TypeScript?
Зачем нужны 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 при просьбе обобщить паттерн


