Какие helper типы использовал в TypeScript?
Комментарии (1)
Ответ сгенерирован нейросетью и может содержать ошибки
Мои наиболее часто используемые TypeScript Helper Types
За годы работы с TypeScript я выработал набор helper-типов, которые стали неотъемлемой частью моего ежедневного арсенала. Эти типы не просто синтаксический сахар — они фундаментально меняют подход к типобезопасности, делая код более выразительным и менее подверженным ошибкам.
Встроенные Utility Types (самая часто используемая группа)
TypeScript предоставляет богатую стандартную библиотеку utility-типов, которые я использую практически в каждом проекте:
Partial<T> — создаёт тип со всеми свойствами T, но делая их необязательными. Идеален для параметров обновления объектов:
interface User {
id: number;
name: string;
email: string;
}
function updateUser(id: number, updates: Partial<User>) {
// Можно передать только name, только email или оба
}
updateUser(1, { name: "Иван" }); // OK
Required<T> — противоположность Partial, делает все свойства обязательными. Особенно полезен при работе с конфигурациями, где значения по умолчанию уже применены:
interface Config {
apiUrl?: string;
timeout?: number;
}
function initializeApp(config: Required<Config>) {
// Гарантировано есть и apiUrl, и timeout
}
Pick<T, K> и Omit<T, K> — для композиции типов. Pick берёт только указанные свойства, Omit — исключает указанные:
type UserPreview = Pick<User, "id" | "name">;
// { id: number; name: string }
type UserWithoutId = Omit<User, "id">;
// { name: string; email: string }
Record<K, T> — создаёт тип объекта с ключами типа K и значениями типа T. Незаменим для словарей и маппингов:
type PageTitles = Record<"home" | "about" | "contact", string>;
// Эквивалентно: { home: string; about: string; contact: string }
const titles: PageTitles = {
home: "Главная",
about: "О нас",
contact: "Контакты"
};
ReturnType<T> и Parameters<T> — для работы с сигнатурами функций. Позволяют извлекать типы возвращаемых значений и параметров:
function fetchUser(id: number): Promise<User> {
/* ... */
}
type FetchResult = ReturnType<typeof fetchUser>; // Promise<User>
type FetchParams = Parameters<typeof fetchUser>; // [number]
Продвинутые кастомные helper-типы
Помимо стандартных, я часто создаю свои специализированные типы:
Nullable<T> — для явного указания nullable-типов (особенно полезно при интеграции с GraphQL или API, где поля могут быть null):
type Nullable<T> = T | null;
type Maybe<T> = T | null | undefined;
interface Product {
id: number;
name: string;
description: Nullable<string>; // Явно показывает, что может быть null
}
ValueOf<T> — для извлечения типов значений объекта (аналогично Object.values() в рантайме):
type ValueOf<T> = T[keyof T];
const Status = {
PENDING: "pending",
SUCCESS: "success",
ERROR: "error"
} as const;
type StatusType = ValueOf<typeof Status>; // "pending" | "success" | "error"
DeepPartial<T> и DeepReadonly<T> — рекурсивные версии стандартных типов для работы с вложенными структурами:
type DeepPartial<T> = {
[P in keyof T]?: T[P] extends object ? DeepPartial<T[P]> : T[P];
};
interface Company {
name: string;
address: {
city: string;
street: string;
};
}
const partialCompany: DeepPartial<Company> = {
address: {
city: "Москва" // Можно указать только city без street
}
};
Специализированные helper-типы для конкретных задач
Для форм и валидации:
type FormErrors<T> = Partial<Record<keyof T, string>>;
type TouchedFields<T> = Partial<Record<keyof T, boolean>>;
Для экшенов Redux/State Management:
type ActionType<T extends { [key: string]: (...args: any[]) => any }> =
ReturnType<T[keyof T]>;
Для условных рендеров в React:
type PropsWithConditionalRender<T> = T & {
isLoading?: boolean;
isError?: boolean;
error?: Error;
};
Ключевые принципы использования helper-типов
-
Документирование намерений — helper-типы делают намерения разработчика явными.
Partial<UserUpdate>понятнее, чем ручное описание всех полей как необязательных. -
DRY (Don't Repeat Yourself) — позволяют избежать дублирования определений типов и синхронизировать связанные типы автоматически.
-
Адаптивность к изменениям — при изменении базового типа все производные типы обновляются автоматически.
-
Более строгие контракты — такие типы как
RequiredиNonNullableпозволяют выражать более точные требования к данным на уровне типизации.
На практике я часто комбинирую несколько helper-типов для создания сложных, но точных определений:
type ApiResponse<T> = {
data: T;
meta: Record<string, unknown>;
};
type UserApiResponse = ApiResponse<Partial<User>>;
Главный совет — не создавать helper-типы "на будущее", а вводить их по мере возникновения конкретной необходимости, когда становится очевидным повторяющийся паттерн в кодовой базе. Хороший helper-тип решает конкретную проблему типизации, а не просто добавляет абстракцию.