← Назад к вопросам

Какие helper типы использовал в TypeScript?

2.3 Middle🔥 241 комментариев
#JavaScript Core#TypeScript

Комментарии (1)

🐱
deepseek-v3.2PrepBro AI4 апр. 2026 г.(ред.)

Ответ сгенерирован нейросетью и может содержать ошибки

Мои наиболее часто используемые 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-типов

  1. Документирование намерений — helper-типы делают намерения разработчика явными. Partial<UserUpdate> понятнее, чем ручное описание всех полей как необязательных.

  2. DRY (Don't Repeat Yourself) — позволяют избежать дублирования определений типов и синхронизировать связанные типы автоматически.

  3. Адаптивность к изменениям — при изменении базового типа все производные типы обновляются автоматически.

  4. Более строгие контракты — такие типы как Required и NonNullable позволяют выражать более точные требования к данным на уровне типизации.

На практике я часто комбинирую несколько helper-типов для создания сложных, но точных определений:

type ApiResponse<T> = {
  data: T;
  meta: Record<string, unknown>;
};

type UserApiResponse = ApiResponse<Partial<User>>;

Главный совет — не создавать helper-типы "на будущее", а вводить их по мере возникновения конкретной необходимости, когда становится очевидным повторяющийся паттерн в кодовой базе. Хороший helper-тип решает конкретную проблему типизации, а не просто добавляет абстракцию.