Владислав Калачев

TypeScript Utility Types: Как использовать встроенные утилиты для упрощения кода

7

TypeScript предлагает мощный набор утилитарных типов, которые позволяют работать с существующими типами, избегая дублирования кода и улучшая читаемость. Они помогают сократить объем кода, сделать его более предсказуемым и упростить поддержку сложных приложений.

Когда я только начинал работать с TypeScript, мне казалось, что основные преимущества языка — это строгая типизация и интерфейсы. Но по мере роста проектов и увеличения количества типов стало очевидно, что без утилитарных типов код становится громоздким, дублирующимся и трудночитаемым. Вместо того чтобы вручную переопределять частичные версии объектов или фильтровать их свойства, можно использовать встроенные инструменты, которые делают это автоматически.

Эта статья посвящена наиболее полезным утилитарным типам TypeScript, которые я активно использую в реальных проектах. Я покажу, как они работают, и приведу примеры, демонстрирующие их практическое применение.

Partial<T>: Делает все свойства опциональными

Иногда нужно создать версию объекта, где все поля необязательны. Например, у нас есть интерфейс User, и нам нужна функция обновления пользователя:

interface User {
  id: number;
  name: string;
  email: string;
}

function updateUser(id: number, updates: Partial) {
  // Здесь мы можем передавать только те поля, которые хотим обновить
  console.log(`Обновляем пользователя ${id} с данными`, updates);
}

updateUser(1, { name: "Новый пользователь" });

Здесь Partial<User> позволяет передавать только часть свойств.

Required<T>: Делает все свойства обязательными

Обратная ситуация — у нас есть объект, где некоторые свойства могут отсутствовать, но в определённый момент мы хотим, чтобы они были обязательно заполнены.

interface Config {
  url?: string;
  timeout?: number;
}

const defaultConfig: Required = {
  url: "https://api.example.com",
  timeout: 5000,
};

Здесь Required<Config> гарантирует, что все свойства url и timeout будут заданы.

Pick<T, K>: Выбирает только нужные свойства

Допустим, у нас есть большой интерфейс, но в какой-то функции нужны только определённые поля.

interface User {
  id: number;
  name: string;
  email: string;
  age: number;
}

type UserPreview = Pick;

const user: UserPreview = {
  id: 1,
  name: "Андрей",
};

Это полезно, когда мы работаем с API и передаем только часть данных.

Omit<T, K>: Убирает ненужные свойства

Аналогично Pick, но наоборот — исключает указанные свойства:

type UserWithoutEmail = Omit;

const user: UserWithoutEmail = {
  id: 1,
  name: "Андрей",
  age: 30,
};

Это удобно, например, когда нужно скрыть чувствительные данные перед отправкой клиенту.

Readonly<T>: Делает объект неизменяемым

Иногда нужно защититься от случайного изменения объекта:

const user: Readonly = {
  id: 1,
  name: "Андрей",
  email: "andrey@example.com",
};

// user.name = "Иван"; // Ошибка: свойство name доступно только для чтения

Используется, когда объект должен оставаться неизменным после создания.

Record<K, T>: Создает объект с заранее известными ключами

Если нам нужно описать объект, в котором ключи и значения имеют строгий тип:

type Role = "admin" | "user" | "guest";

const permissions: Record = {
  admin: ["create", "edit", "delete"],
  user: ["read", "comment"],
  guest: ["read"],
};

Record удобно использовать при маппинге значений по фиксированному набору ключей.

Extract<T, U> и Exclude<T, U>: Работа с union-типами

Когда у нас есть union-тип, и мы хотим либо оставить только определённые значения, либо убрать их:

type Status = "pending" | "success" | "error";

type SuccessStatus = Extract; // "success"
type ErrorStatus = Exclude; // "pending" | "error"

Extract выбирает только переданные значения, а Exclude, наоборот, удаляет их.

Заключение

TypeScript предлагает мощные утилитарные типы, которые помогают сократить код и сделать его более выразительным. В реальных проектах они позволяют:
— Снижать дублирование типов (Pick, Omit), что особенно полезно при проектировании API и работе с моделями данных.
— Делать код более защищённым (Readonly, Required), помогая предотвратить случайные изменения данных.
— Гибко работать с данными (Partial, Record), что облегчает создание динамических структур.
— Манипулировать union-типами (Extract, Exclude), позволяя создавать точные ограничения на уровне типов.

Эти утилиты особенно полезны при разработке сложных фронтенд-приложений, где строгая типизация помогает избежать множества ошибок ещё на этапе компиляции.

Лично я регулярно использую Pick, Omit и Partial при работе с API, а Record отлично подходит для хранения конфигураций и прав доступа. Знание этих инструментов существенно упрощает поддержку крупных кодовых баз и делает код более понятным и предсказуемым.

Если вы ещё не используете утилиты TypeScript активно, советую поэкспериментировать и найти, какие из них будут наиболее полезны в ваших проектах. Надеюсь, примеры помогли разобраться, как применять эти утилиты на практике!

Комментарии:

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *