TypeScript Преобразованные типы

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

  • Преобразованные типы = трансформация каждого свойства типа.
  • Общие типы: Partial, Readonly, Pick, Omit, Record.

Пример


// Простой пример
type Person = { name: string; age: number };
type PartialPerson = { [P in keyof Person]?: Person[P] };
type ReadonlyPerson = { readonly [P in keyof Person]: Person[P] };


Базовый синтаксис преобразованных типов

Основной синтаксис

Преобразованные типы используют синтаксис { [P in K]: T }, где:

  • P — имя свойства, по которому идёт итерация;
  • K — объединение имён свойств, по которым выполняется итерация;
  • T — результирующий тип для каждого свойства.

Пример


// Определяем тип объекта
interface Person {
  name: string;
  age: number;
  email: string;
}

// Создаём преобразованный тип, делающий все свойства опциональными
type PartialPerson = {
  [P in keyof Person]?: Person[P];
};

// Использование
const partialPerson: PartialPerson = {
  name: "John"
  // age и email — опциональные свойства
};

// Создаём преобразованный тип, делающий все свойства доступными только для чтения
type ReadonlyPerson = {
  readonly [P in keyof Person]: Person[P];
};

// Использование
const readonlyPerson: ReadonlyPerson = {
  name: "Alice",
  age: 30,
  email: "alice@example.com"
};

// readonlyPerson.age = 31; // Ошибка: нельзя присвоить значение свойству 'age', так как оно доступно только для чтения


Встроенные преобразованные типы

Утилиты стандартной библиотеки

В TypeScript есть несколько полезных встроенных преобразованных типов:

  • Partial<T> — делает все свойства опциональными;
  • Readonly<T> — делает все свойства доступными только для чтения;
  • Pick<T, K> — выбирает подмножество ключей;
  • Omit<T, K> — удаляет указанные ключи;
  • Record<K, V> — сопоставляет ключи с типом значения.

Пример


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

// Partial<T> — делает все свойства опциональными
type PartialUser = Partial<User>;
// Эквивалентно: { id?: number; name?: string; email?: string; isAdmin?: boolean; }

// Required<T> — делает все свойства обязательными
type RequiredUser = Required<Partial<User>>;
// Эквивалентно: { id: number; name: string; email: string; isAdmin: boolean; }

// Readonly<T> — делает все свойства доступными только для чтения
type ReadonlyUser = Readonly<User>;
// Эквивалентно: { readonly id: number; readonly name: string; ... }

// Pick<T, K> — создаёт тип с подмножеством свойств из T
type UserCredentials = Pick<User, "email" | "id">;
// Эквивалентно: { email: string; id: number; }

// Omit<T, K> — создаёт тип, удаляя указанные свойства из T
type PublicUser = Omit<User, "id" | "isAdmin">;
// Эквивалентно: { name: string; email: string; }

// Record<K, T> — создаёт тип с указанными ключами и типами значений
type UserRoles = Record<"admin" | "user" | "guest", string>;
// Эквивалентно: { admin: string; user: string; guest: string; }


Создание пользовательских преобразованных типов

Базовые пользовательские преобразователи

Вы можете создавать собственные преобразованные типы для трансформации типов определённым образом.

Пример


// Базовый интерфейс
interface Product {
  id: number;
  name: string;
  price: number;
  inStock: boolean;
}

// Создаём преобразованный тип для трансформации всех свойств в тип string
type StringifyProperties<T> = {
  [P in keyof T]: string;
};

// Использование
type StringProduct = StringifyProperties<Product>;
// Эквивалентно: { id: string; name: string; price: string; inStock: string; }

// Создаём преобразованный тип, добавляющий функции валидации для каждого свойства
type Validator<T> = {
  [P in keyof T]: (value: T[P]) => boolean;
};

// Использование
const productValidator: Validator<Product> = {
  id: (id) => id > 0,
  name: (name) => name.length > 0,
  price: (price) => price >= 0,
  inStock: (inStock) => typeof inStock === "boolean"
};


Изменение модификаторов свойств

Добавление и удаление модификаторов

Преобразованные типы также позволяют добавлять или удалять модификаторы свойств, такие как readonly и ? (опциональность).

Пример


// Базовый интерфейс с некоторыми readonly и опциональными свойствами
interface Configuration {
  readonly apiKey: string;
  readonly apiUrl: string;
  timeout?: number;
  retries?: number;
}

// Удаляем модификатор readonly у всех свойств
type Mutable<T> = {
  -readonly [P in keyof T]: T[P];
};

// Использование
type MutableConfig = Mutable<Configuration>;
// Эквивалентно: { apiKey: string; apiUrl: string; timeout?: number; retries?: number; }

// Делаем все опциональные свойства обязательными
type RequiredProps<T> = {
  [P in keyof T]-?: T[P];
};

// Использование
type RequiredConfig = RequiredProps<Configuration>;
// Эквивалентно: { readonly apiKey: string; readonly apiUrl: string; timeout: number; retries: number; }


Продвинутые преобразованные типы

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

Преобразованные типы становятся ещё мощнее при сочетании с условными типами.

Пример


// Базовый интерфейс
interface ApiResponse {
  data: unknown;
  status: number;
  message: string;
  timestamp: number;
}

// Условный преобразованный тип: преобразуем каждое числовое свойство в форматированную строку
type FormattedResponse<T> = {
  [P in keyof T]: T[P] extends number ? string : T[P];
};

// Использование
type FormattedApiResponse = FormattedResponse<ApiResponse>;
// Эквивалентно: { data: unknown; status: string; message: string; timestamp: string; }

// Ещё пример: фильтруем только строковые свойства
type StringPropsOnly<T> = {
  [P in keyof T as T[P] extends string ? P : never]: T[P];
};

// Использование
type ApiResponseStringProps = StringPropsOnly<ApiResponse>;
// Эквивалентно: { message: string; }


Ключевые выводы

Преобразованные типы позволяют единообразно трансформировать каждое свойство типа.

Ключевые концепции

  • Трансформация типов: массовое изменение типов свойств;
  • Модификаторы свойств: добавление или удаление модификаторов readonly и ?;
  • Преобразование ключей: переименование или фильтрация свойств с помощью конструкций as;
  • Композиция: сочетание с другими возможностями TypeScript.

Типичные сценарии применения

  • Создание версий типов только для чтения;
  • Трансформация всех свойств в опциональные или обязательные;
  • Трансформация типов свойств (например, в nullable или readonly);
  • Фильтрация свойств на основе их типов;
  • Создание типобезопасных вспомогательных функций.