🎯 Зачем спрашивают
- Проверить знание стандартных инструментов TypeScript, которые повседневно используют.
- Убедиться, что кандидат понимает, что utility types — это просто generics + mapped types + conditional types под капотом, а не «магия».
- Понять, умеет ли кандидат читать и писать собственные утилиты, т.е. насколько глубоко он понимает типовую систему TS.
📝 Ответ
Utility types — это набор уже готовых (generic) типов для различных типовых задач: трансформация, модификация и комбинирование типов.
Популярные utility types:
Partial<T>
Делает все свойства типа
T необязательными.type User = { id: number; name: string }; type PartialUser = Partial<User>; // { id?: number; name?: string }
Required<T>
Обратный к
Partial — все свойства обязательные.type PartialUser = { id?: number; name?: string }; type FullUser = Required<PartialUser>; // { id: number; name: string }
Readonly<T>
Все свойства становятся только для чтения.
type User = { id: number; name: string }; type ReadonlyUser = Readonly<User>; // { readonly id: number; readonly name: string }
Pick<T, K>
Создаёт тип только с выбранными свойствами.
type User = { id: number; name: string; age: number }; type UserPreview = Pick<User, "id" | "name">; // { id: number; name: string }
Omit<T, K>
Противоположен
Pick — исключает указанные свойства.type User = { id: number; name: string; age: number }; type UserWithoutAge = Omit<User, "age">; // { id: number; name: string }
Record<K, T>
Создаёт объектный тип, где ключи
K имеют значения типа T.type Roles = "admin" | "user" | "guest"; type RolePermissions = Record<Roles, boolean>; // { admin: boolean; user: boolean; guest: boolean }
Так же вы можете создавать свои собственные utility types.
⚖️ Компромиссы
- ✅ Плюсы: экономия времени, повышение читаемости, меньше boilerplate.
- ❌ Минусы:
- Чрезмерное увлечение утилитами может ухудшать читаемость (особенно вложенные конструкции).
- Иногда проще описать новый тип, чем комбинировать 3 утилиты.
🔎 Встречные вопросы
- Как реализован
Partial<T>под капотом?
- Чем
Pickотличается отOmit?
- В чём разница между
ExcludeиOmit?
- Что такое «distributive conditional types» и почему
Excludeработает именно так?
🚩 Красные флаги
- Отвечают только «ну, это встроенные типы для TS».
- Перечисляют только 1–2 (
Partial,Pick) и не могут показать примеры.
- Считают, что утилиты — это что-то «отдельное» от TypeScript generics (а на самом деле они просто «шорткаты» на conditional types + mapped types).
- А зачем мне эти типы? Я просто копипастну тип, да и отредактирую
🛠 Практика
Реализовать MyPartial<T>
type User = { id: number; name: string }; type Test = MyPartial<User>; // { id?: number; name?: string }
Реализовать MyRequired<T>
type User = { id?: number; name?: string }; type Test = MyRequired<User>; // { id: number; name: string }
Реализовать MyReadonly<T>
type User = { id: number; name: string }; type Test = MyReadonly<User>; // { readonly id: number; readonly name: string }
Реализовать MyPick<T, K>
type User = { id: number; name: string; age: number }; type Test = MyPick<User, "id" | "name">; // { id: number; name: string }
Реализовать MyOmit<T, K>
type User = { id: number; name: string; age: number }; type Test = MyOmit<User, "age">; // { id: number; name: string }
Реализовать MyKeys<T>
type User = { id: number; name: string; age: number; }; type Keys = MyKeys<User>; // "id" | "name" | "age"
Реализовать DeepKeys<T>/
type User = { id: number; name: string; address: { city: string; street: string; }; }; type DeepKeys<User>; // "id" | "name" | "address" | "city" | "street"
📚 Источники / ссылки
- Utility Types, typescriptlang.org