Что такое utility types в TypeScript?
Utility Types в TypeScript
Utility types — встроенные вспомогательные типы, реализованные через дженерики. Они позволяют создавать новые типы на основе существующих без дублирования кода.
Основные utility types
Partial<T> — делает все поля типа необязательными:
interface User {
id: number;
name: string;
email: string;
}
// Все поля становятся опциональными
type PartialUser = Partial<User>;
// { id?: number; name?: string; email?: string }
Required<T> — противоположность Partial, делает все поля обязательными.
Readonly<T> — запрещает изменение полей после создания объекта:
const config: Readonly<User> = { id: 1, name: 'Иван', email: 'i@i.ru' };
config.name = 'Пётр'; // Ошибка: нельзя изменить readonly свойство
Pick<T, K> — выбирает только указанные ключи из типа:
type UserPreview = Pick<User, 'id' | 'name'>;
// { id: number; name: string }
Omit<T, K> — исключает указанные ключи из типа:
type UserWithoutEmail = Omit<User, 'email'>;
// { id: number; name: string }
Record<K, V> — создаёт тип объекта с ключами K и значениями V:
type RoleMap = Record<'admin' | 'user' | 'guest', boolean>;
// { admin: boolean; user: boolean; guest: boolean }
Exclude<T, U> и Extract<T, U> — работают с union-типами:
type Status = 'active' | 'inactive' | 'banned';
type ActiveStatus = Exclude<Status, 'banned'>;
// 'active' | 'inactive'
type BannedOnly = Extract<Status, 'banned' | 'inactive'>;
// 'banned' | 'inactive'
NonNullable<T> — исключает null и undefined из типа:
type MaybeString = string | null | undefined;
type DefinitelyString = NonNullable<MaybeString>; // string
ReturnType<T> и Parameters<T> — извлекают типы из функций:
function getUser(id: number): User { /* ... */ }
type GetUserReturn = ReturnType<typeof getUser>; // User
type GetUserParams = Parameters<typeof getUser>; // [id: number]
Практическое применение
Utility types особенно полезны при работе с API и формами:
// Тип для создания пользователя — без id (его генерирует сервер)
type CreateUserDto = Omit<User, 'id'>;
// Тип для обновления — все поля опциональны
type UpdateUserDto = Partial<Omit<User, 'id'>>;
Как они устроены внутри
Bольшинство utility types реализованы через mapped types и conditional types:
// Упрощённая реализация Partial
type MyPartial<T> = {
[K in keyof T]?: T[K];
};
// Упрощённая реализация Readonly
type MyReadonly<T> = {
readonly [K in keyof T]: T[K];
};
Понимание внутреннего устройства позволяет создавать собственные utility types под специфические нужды проекта.
Что хочет услышать интервьюер
Кандидат перечисляет хотя бы 5–7 встроенных utility types (Partial, Required, Readonly, Pick, Omit, Record, Exclude, Extract)
Умеет объяснить разницу между Pick/Omit и Exclude/Extract (первые работают с объектами, вторые — с union-типами)
Приводит реальные примеры использования в контексте DTO, форм или API-запросов
Понимает, что utility types реализованы через mapped types и conditional types
Знает ReturnType и Parameters для работы с типами функций
Пример: Основные utility types
interface User {
id: number;
name: string;
email: string;
role: 'admin' | 'user';
}
// Partial — все поля опциональны
type UpdateUserDto = Partial<User>;
// Pick — только нужные поля
type UserPreview = Pick<User, 'id' | 'name'>;
// Omit — исключить поля
type CreateUserDto = Omit<User, 'id'>;
// Record — словарь
type PermissionMap = Record<User['role'], string[]>;
const permissions: PermissionMap = {
admin: ['read', 'write', 'delete'],
user: ['read'],
};
// Readonly — запрет мутаций
const frozenUser: Readonly<User> = {
id: 1, name: 'Иван', email: 'ivan@mail.ru', role: 'user'
};
Пример: Exclude, Extract, NonNullable
type Status = 'active' | 'inactive' | 'banned' | null;
// Убираем null и конкретные значения
type ActiveStatus = Exclude<NonNullable<Status>, 'banned'>;
// 'active' | 'inactive'
// Оставляем только нужные значения
type NegativeStatus = Extract<Status, 'inactive' | 'banned'>;
// 'inactive' | 'banned'
Пример: ReturnType и Parameters
async function fetchUser(id: number, withRoles: boolean): Promise<User> {
// имитация запроса к API
return { id, name: 'Иван', email: 'ivan@mail.ru', role: 'user' };
}
// Извлекаем тип возвращаемого значения
type FetchUserResult = Awaited<ReturnType<typeof fetchUser>>; // User
// Извлекаем типы параметров
type FetchUserParams = Parameters<typeof fetchUser>; // [id: number, withRoles: boolean]
Пример: Собственный utility type
// Делаем только указанные поля обязательными, остальные — опциональными
type RequireFields<T, K extends keyof T> = Omit<T, K> & Required<Pick<T, K>>;
type UserWithRequiredEmail = RequireFields<Partial<User>, 'email'>;
// email — обязателен, остальные поля — опциональны
Типичные ошибки
Путают Pick/Omit (для объектных типов) с Exclude/Extract (для union-типов)
Не знают утилиты для работы с функциями: ReturnType, Parameters, ConstructorParameters
Используют Partial везде, где нужен Omit — например, для DTO без id
Не умеют комбинировать utility types (например, Partial<Omit<T, 'id'>>)
Не понимают, как utility types устроены внутри — не могут написать аналог самостоятельно


