TYPESCRIPT
#typescript#utility-types#partial#pick#omit#typing

TypeScript: утилитарные типы для работы с данными

Использование встроенных утилитарных типов TypeScript: Partial, Pick, Omit, Required, Readonly для трансформации типов без дублирования кода.

TypeScript предоставляет встроенные утилитарные типы для трансформации существующих типов. Это избавляет от дублирования кода.

Partial — все поля опциональны

interface User {
  id: number;
  name: string;
  email: string;
  password: string;
  createdAt: Date;
}

// Partial делает все поля опциональными
type PartialUser = Partial<User>;

// Можно обновить только нужные поля
const updateData: PartialUser = {
  name: "New Name" // можно указать только нужные поля
};

// Использование в функции обновления
function updateUser(id: number, data: Partial<User>): void {
  // Обновляем только переданные поля
}

Pick — выбрать только нужные поля

// Выбираем только id, name и email
type PublicUser = Pick<User, "id" | "name" | "email">;

const publicProfile: PublicUser = {
  id: 1,
  name: "John",
  email: "john@example.com"
  // password и createdAt недоступны
};

// Использование для API-ответов
function getUserPublic(id: number): Promise<PublicUser> {
  // Возвращаем только публичные данные
}

Omit — исключить ненужные поля

// Исключаем password из типа
type UserWithoutPassword = Omit<User, "password">;

const safeUser: UserWithoutPassword = {
  id: 1,
  name: "John",
  email: "john@example.com",
  createdAt: new Date()
  // password недоступен
};

// Можно исключить несколько полей
type UserPublic = Omit<User, "password" | "createdAt">;

Required — все поля обязательны

interface Config {
  host?: string;
  port?: number;
  timeout?: number;
}

// Required делает все поля обязательными
type RequiredConfig = Required<Config>;

const config: RequiredConfig = {
  host: "localhost", // обязательно
  port: 3000,        // обязательно
  timeout: 5000     // обязательно
};

Readonly — сделать поля только для чтения

// Все поля становятся readonly
type ReadonlyUser = Readonly<User>;

const user: ReadonlyUser = {
  id: 1,
  name: "John",
  email: "john@example.com",
  password: "secret",
  createdAt: new Date()
};

// user.name = "Jane"; // ❌ Ошибка: нельзя изменить readonly поле

Комбинирование утилитарных типов

// Публичный профиль без пароля и даты создания, только для чтения
type PublicReadonlyUser = Readonly<Omit<User, "password" | "createdAt">>;

// Частичное обновление с обязательными полями
type UserUpdate = Partial<Pick<User, "name" | "email">> & {
  id: number; // id обязателен
};

Record — создание типа из ключей и значений

// Record<K, V> создаёт объект с ключами типа K и значениями типа V
type StatusMap = Record<string, "pending" | "approved" | "rejected">;

const statuses: StatusMap = {
  user1: "pending",
  user2: "approved",
  user3: "rejected"
};

// С конкретными ключами
type UserRoles = Record<"admin" | "user" | "guest", string[]>;

Extract и Exclude — работа с union типами

type Status = "pending" | "approved" | "rejected" | "spam";

// Extract — выбрать только определённые типы
type ActiveStatus = Extract<Status, "pending" | "approved">; // "pending" | "approved"

// Exclude — исключить определённые типы
type CleanStatus = Exclude<Status, "spam">; // "pending" | "approved" | "rejected"

NonNullable — исключить null и undefined

type MaybeString = string | null | undefined;

// NonNullable исключает null и undefined
type DefiniteString = NonNullable<MaybeString>; // string

function processString(str: NonNullable<string | null>): void {
  // str гарантированно string, не null
}

Usage:

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

Notes:

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