Комментарии (1)
🐱
claude-haiku-4.5PrepBro AI26 мар. 2026 г.(ред.)
Ответ сгенерирован нейросетью и может содержать ошибки
Для чего нужны Utility Types?
Utility Types в TypeScript — это встроенные обобщённые типы, которые позволяют преобразовывать и модифицировать существующие типы, не переписывая их с нуля. Это мощный инструмент для создания типов на основе других типов, снижая дублирование кода и повышая переиспользуемость.
Основная идея
Вместо того чтобы создавать похожие типы вручную, используй Utility Types для трансформации:
// ❌ Плохо: много дублирования
interface User {
id: number;
name: string;
email: string;
}
interface PartialUser {
id?: number;
name?: string;
email?: string;
}
interface ReadonlyUser {
readonly id: number;
readonly name: string;
readonly email: string;
}
// ✅ Хорошо: используем Utility Types
type PartialUser = Partial<User>;
type ReadonlyUser = Readonly<User>;
Популярные Utility Types
1. Partial<T> — все поля опциональны
interface User {
id: number;
name: string;
email: string;
}
type PartialUser = Partial<User>;
// Эквивалент:
// {
// id?: number;
// name?: string;
// email?: string;
// }
const updateUser: PartialUser = {
name: 'John' // только name, остальное опционально
};
2. Required<T> — все поля обязательны
type PartialUser = {
id?: number;
name?: string;
email?: string;
};
type RequiredUser = Required<PartialUser>;
// Эквивалент:
// {
// id: number;
// name: string;
// email: string;
// }
const user: RequiredUser = {
id: 1,
name: 'John',
email: 'john@example.com' // все поля обязательны
};
3. Readonly<T> — все поля неизменяемы
interface User {
id: number;
name: string;
}
type ReadonlyUser = Readonly<User>;
const user: ReadonlyUser = { id: 1, name: 'John' };
user.name = 'Jane'; // ❌ Ошибка: Cannot assign to 'name' because it is a read-only property
4. Pick<T, K> — выбрать определённые поля
interface User {
id: number;
name: string;
email: string;
age: number;
}
type UserPreview = Pick<User, 'id' | 'name'>;
// Эквивалент: { id: number; name: string }
const preview: UserPreview = {
id: 1,
name: 'John'
// email и age НЕ требуются
};
5. Omit<T, K> — исключить определённые поля
interface User {
id: number;
name: string;
email: string;
password: string;
}
type PublicUser = Omit<User, 'password'>;
// Эквивалент: { id: number; name: string; email: string }
const publicUser: PublicUser = {
id: 1,
name: 'John',
email: 'john@example.com'
// password исключена
};
6. Record<K, T> — создать объект с определёнными ключами
type Status = 'active' | 'inactive' | 'pending';
type StatusConfig = Record<Status, { label: string; color: string }>;
const config: StatusConfig = {
active: { label: 'Active', color: 'green' },
inactive: { label: 'Inactive', color: 'gray' },
pending: { label: 'Pending', color: 'yellow' }
// TypeScript проверит, что все статусы есть!
};
7. Extract<T, U> — извлечь пересекающиеся типы
type Status = 'active' | 'inactive' | 'pending' | 'deleted';
type ActionableStatus = Extract<Status, 'active' | 'inactive'>;
// Эквивалент: 'active' | 'inactive'
const status: ActionableStatus = 'active'; // ✅ OK
const status2: ActionableStatus = 'deleted'; // ❌ Ошибка
8. Exclude<T, U> — исключить типы
type Status = 'active' | 'inactive' | 'pending' | 'deleted';
type VisibleStatus = Exclude<Status, 'deleted'>;
// Эквивалент: 'active' | 'inactive' | 'pending'
const status: VisibleStatus = 'deleted'; // ❌ Ошибка
9. Keyof<T> — получить ключи типа
interface User {
id: number;
name: string;
email: string;
}
type UserKeys = keyof User;
// Эквивалент: 'id' | 'name' | 'email'
const key: UserKeys = 'name'; // ✅ OK
const key2: UserKeys = 'age'; // ❌ Ошибка
10. ReturnType<T> — получить тип возврата функции
function getUser(): { id: number; name: string } {
return { id: 1, name: 'John' };
}
type UserType = ReturnType<typeof getUser>;
// Эквивалент: { id: number; name: string }
Практические примеры
Пример 1: API Request и Response
interface CreateUserRequest {
name: string;
email: string;
password: string;
}
// PublicUser скрывает password
type UserResponse = Omit<CreateUserRequest, 'password'>;
const handleCreateUser = async (
data: CreateUserRequest
): Promise<UserResponse> => {
// ... сохраняем пользователя
const { password, ...user } = data;
return user; // password не отправляется
};
Пример 2: Form Validation
interface User {
id: number;
name: string;
email: string;
age: number;
}
// Для формы редактирования требуются только некоторые поля
type UserFormData = Partial<Pick<User, 'name' | 'email'>>;
const handleFormSubmit = (data: UserFormData) => {
// data может содержать только name, только email, или оба
};
Пример 3: Configuration Object
interface AppConfig {
apiUrl: string;
apiKey: string;
timeout: number;
retries: number;
}
// Некоторые параметры опциональны при создании
type PartialConfig = Partial<AppConfig>;
type DefaultConfig = Required<AppConfig>; // все обязательны
const createApp = (config: PartialConfig): AppConfig => {
return {
apiUrl: config.apiUrl || 'https://api.example.com',
apiKey: config.apiKey || process.env.API_KEY!,
timeout: config.timeout || 5000,
retries: config.retries || 3
};
};
Пример 4: Event Handler Typing
type Events = {
'user-login': { userId: number };
'user-logout': { timestamp: number };
'error': { message: string; code: number };
};
type EventHandlers = Record<keyof Events, (data: Events[keyof Events]) => void>;
// TypeScript проверит, что все события обработаны
const handlers: EventHandlers = {
'user-login': (data) => console.log(data.userId),
'user-logout': (data) => console.log(data.timestamp),
'error': (data) => console.error(data.message, data.code)
};
Создание собственных Utility Types
// Кастомный Utility Type: получить все необязательные ключи
type Optional<T> = {
[K in keyof T]?: T[K];
};
// Кастомный: сделать все поля строками
type Stringify<T> = {
[K in keyof T]: string;
};
// Кастомный: сделать свойства функциями
type Callable<T> = {
[K in keyof T]: () => T[K];
};
interface User {
id: number;
name: string;
}
type CallableUser = Callable<User>;
// {
// id: () => number;
// name: () => string;
// }
Когда использовать Utility Types?
✅ Используй, если:
- Нужно избежать дублирования типов
- Трансформируешь существующие типы
- Работаешь с API ответами
- Создаёшь generics
❌ Не используй, если:
- Проще написать тип с нуля
- Трансформация очень специфична
- Усложняет читаемость кода
Лучшие практики
- Комбинируй Utility Types для сложных трансформаций:
type UpdateUserDTO = Partial<Omit<User, 'id' | 'createdAt'>>;
- Документируй кастомные Utility Types:
/** Убирает поле password из User для публичного использования */
type PublicUser = Omit<User, 'password'>;
- Используй Pick для API контрактов:
type UserListItem = Pick<User, 'id' | 'name' | 'avatar'>;
Utility Types — это мощный инструмент для написания DRY, типобезопасного и поддерживаемого TypeScript кода.