TypeScript: основные utility‑типы и примеры
TypeScript расширяет JavaScript статической типизацией и содержит встроенные utility-типы, упрощающие работу с типами. В этой статье собраны основные utility-типы (Partial, Pick, Readonly, Required, Omit, Record), их примеры, когда их использовать, альтернативы и практические чек-листы.
Введение
TypeScript — надмножество JavaScript, которое добавляет статическую типизацию. Utility-типы в TypeScript — это готовые обобщённые типы, которые помогают трансформировать существующие интерфейсы и типы без повторения кода. Они повышают читаемость, уменьшают количество ошибок и ускоряют рефакторинг.
Важно: в одной статье приведены практические подсказки, типичные ошибки и альтернативы. Определение терминов: utility‑тип — предопределённый тип в TypeScript, который принимает другие типы и возвращает новый тип.
Partial — сделать свойства необязательными
Описание
Partial
Пример:
interface User {
name: string;
age: number;
email: string;
}
type OptionalUser = Partial;
const user: OptionalUser = { name: "John" }; Когда использовать
- Формы с частичным заполнением.
- Функции, принимающие патчи (patch) для объекта.
Ограничения и подводные камни
- Partial делает свойства необязательными, но не меняет их типы — если свойство было сложным (вложенным объектом), его вложенные поля остаются прежними. Для глубокого Partial нужен рекурсивный тип.
Альтернативы
- Создать собственный тип с нужными optional-полями вручную.
- Использовать Pick для точного выбора свойств.
Пример глубокой версии (упрощённо):
type DeepPartial = {
[P in keyof T]?: T[P] extends object ? DeepPartial : T[P];
}; Pick — выбрать подмножество свойств
Описание
Pick
Пример:
interface User {
location: string;
age: number;
email: string;
}
type PersonWithoutEmail = Pick;
const person: PersonWithoutEmail = {
location: 'USA',
age: 30
}; Когда использовать
- Ограничение возвращаемого API-фрагмента.
- Интерфейсы данных для отображения в UI, когда нужны только определённые поля.
Когда Pick не подходит
- Если нужно исключить несколько полей, удобнее Omit.
- Если требуется переименовать ключи — придётся использовать дополнительные маппинги.
Альтернатива: Omit для исключения свойств (см. ниже).
Readonly — сделать свойства неизменяемыми
Описание
Readonly
Пример:
interface User {
name: string;
age: number;
email: string;
}
type ReadonlyUser = Readonly;
const user: ReadonlyUser = {
name: "John",
age: 30,
email: "john@example.com"
};
// user.name = "Jane"; // Ошибка: нельзя присвоить, свойство только для чтения Когда использовать
- Константные конфигурации.
- Объекты, которые передаются между модулями и не должны изменяться.
Ограничения
- Readonly делает свойства поверхностно неизменяемыми; для глубокой неизменности нужен рекурсивный тип DeepReadonly.
Required — все свойства обязательны
Описание
Required
Пример с корректными типами:
interface User {
name: string;
location: string;
address: string;
}
type RequiredUser = Required;
const user: RequiredUser = {
name: "John Doe",
location: "USA",
address: "Kansas 9745-0622"
}; Когда использовать
- Валидировать, что после некоторой стадии все поля заполнены.
- Приведение частичных данных к полному типу перед сохранением.
Когда Required не поможет
- Если нужны условно обязательные поля (зависимость между полями). Для этого нужны условные типы и валидация на уровне логики.
Omit — исключить свойства из типа
Описание
Omit
Пример (исправлено — корректный синтаксис):
interface Person {
location: string;
age: number;
email: string;
}
type PersonWithoutEmail = Omit;
const person: PersonWithoutEmail = { location: "USA", age: 30 }; Когда использовать
- Исключение внутренних полей (паролей, токенов) из публичного типа.
- Создание DTO без определённого набора полей.
Совпадение с Pick
Omit часто используется как противоположность Pick: Omit
Record — задать типы для ключей и значений
Описание
Record
Определение:
type Record = { [P in K]: T }; Пример:
type MyRecord = Record;
const myObject: MyRecord = {
"foo": 1,
"bar": 2,
"baz": 3,
}; Когда использовать
- Маппинг по ключам со строгой типизацией (например, перевод строк, метрики, конфигурации для окружений).
- Когда набор ключей известен заранее — используйте union литералов вместо string для безопасных ключей.
Пример с union ключей:
type Sizes = 'small' | 'medium' | 'large';
type SizeMap = Record;
const sizeMap: SizeMap = { small: 1, medium: 2, large: 3 }; Практическая методология выбора utility-типа
Краткий план при выборе типа:
- Определите, какие свойства вам нужны: все, часть или исключение — Pick/Partial/Omit.
- Нужна ли неизменяемость — Readonly.
- Нужен ли строго определённый набор ключей — Record с union-ключами.
- Нужны ли глубокие преобразования — реализуйте рекурсивные версии (DeepPartial, DeepReadonly).
Мини‑методология (шаблон):
- Для DTO на входе: Partial или Pick.
- Перед сохранением в БД: Required + валидация.
- Для конфигураций: Readonly или Record.
- Для API-ответов: Omit для удаления внутренних полей.
Чек-листы по ролям
Разработчик (frontend)
- Использовать Pick для пропсов компонента с минимально необходимыми полями.
- Для форм использовать Partial или DeepPartial для удобства.
- Не мутировать props — применяйте Readonly, где нужно.
Code reviewer
- Проверить, не раскрываются ли внутренние поля в типах ответа (использовать Omit).
- Убедиться, что Record использует union-ключи, а не plain string, если ключи известны.
Backend/Integration
- Привести внешние данные к Required типам после валидации.
- Для кэша и метрик применять Record с описанными ключами.
Чит‑шит (краткая шпаргалка)
- Partial
— все поля optional. - Required
— все поля обязательны. - Readonly
— все поля readonly. - Pick
— выбрать набор полей K. - Omit
— исключить набор полей K. - Record
— объект с ключами K и значениями T.
Когда utility-типы «не сработают» (примеры ошибок)
- Когда бизнес-правило требует зависимости между полями (например, если A задано, то B обязателен). Utility-типы не моделируют такие зависимости — нужна дополнительная логика или условные типы.
- Глубокие структуры: Partial делает только поверхностные поля optional; для вложенных объектов нужен DeepPartial.
- При переименовании полей: Pick/Omit не переименовывают поля — требуется маппинг.
Ментальные модели и эвристики
- Utility-типы — это трансформеры типа: они берут входной тип и возвращают новый. Представьте их как функции над описаниями структуры данных.
- Всегда думайте про глубину трансформации: shallow vs deep.
- Отдавайте предпочтение сочетанию типов вместо копирования интерфейсов по всему коду — это облегчает рефакторинг.
Решение выбора (flowchart)
flowchart TD
A[Нужна трансформация типа?] -->|да| B{Поменять все свойства?}
B -->|все optional| C[Partial]
B -->|все readonly| D[Readonly]
B -->|все required| E[Required]
B -->|нет| F{Выбрать или исключить поля}
F -->|выбрать| G[Pick]
F -->|исключить| H[Omit]
A -->|нет| I[Оставить исходный тип]
A -->|создать мапу ключ→значение| J[Record]Краткое резюме
Utility-типы ускоряют разработку и улучшают поддержку типов. Часто используются в связке: Pick + Partial, Omit + Required и т. п. Помните про глубину преобразований и про то, что для сложных зависимостей нужны условные или рекурсивные типы.
Важно
- Не придумывайте в типах бизнес-валидацию — типы помогают на ранней стадии, но runtime‑валидация остаётся обязательной.
Ключевые выводы
- Используйте Partial для патчей и форм.
- Используйте Pick/Omit для ограничения видимости полей.
- Используйте Readonly для защиты конфигураций.
- Record удобен для маппинга по фиксированным ключам.
1-строчная глоссарий
- Utility‑тип — предопределённый обобщённый тип в TypeScript, который трансформирует другие типы.
Если нужно, могу подготовить готовые snippets для ESLint/TSConfig или примеры DeepPartial/DeepReadonly для вашего кода.
Похожие материалы
RDP: полный гид по настройке и безопасности
Android как клавиатура и трекпад для Windows
Советы и приёмы для работы с PDF
Calibration в Lightroom Classic: как и когда использовать
Отключить Siri Suggestions на iPhone