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

Можно ли расширить type?

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

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

🐱
claude-haiku-4.5PrepBro AI3 апр. 2026 г.(ред.)

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

Можно ли расширить type

Расширение типов в TypeScript — это создание новых типов на основе существующих. Типы нельзя расширить как классы, но можно создавать новые типы которые их наследуют, комбинируют и расширяют через пересечение и generics.

Основные способы расширения

1. Пересечение типов (&)

Самый распространённый способ расширения типов.

// Базовый тип
type BaseUser = {
  id: string;
  name: string;
};

// Расширяем через пересечение &
type AdminUser = BaseUser & {
  role: 'admin';
  permissions: string[];
};

// Использование - AdminUser имеет все поля BaseUser + новые поля
const admin: AdminUser = {
  id: '1',
  name: 'Alice',      // из BaseUser
  role: 'admin',      // новое
  permissions: []     // новое
};

2. Объединение нескольких типов

type Person = {
  name: string;
  age: number;
};

type Employee = {
  employeeId: string;
  department: string;
};

// Объединяем оба типа в один
type EmployeePerson = Person & Employee;

const emp: EmployeePerson = {
  name: 'Bob',
  age: 30,
  employeeId: 'EMP123',
  department: 'Frontend'
};

3. Union типы для вариантов (|)

type Admin = { role: 'admin'; permissions: string[] };
type User = { role: 'user'; preferences: object };
type Guest = { role: 'guest' };

// Объединение альтернативных типов
type Account = Admin | User | Guest;

const admin: Account = { role: 'admin', permissions: ['edit'] };
const user: Account = { role: 'user', preferences: {} };
const guest: Account = { role: 'guest' };

4. Generics для гибкого расширения

// Базовый generic тип
type Response<T> = {
  status: number;
  data: T;
};

// Расширение с generic
type SuccessResponse<T> = Response<T> & {
  message: 'success';
  timestamp: Date;
};

// Использование с разными типами данных
const userResponse: SuccessResponse<{ id: string; name: string }> = {
  status: 200,
  data: { id: '1', name: 'Alice' },
  message: 'success',
  timestamp: new Date()
};

const listResponse: SuccessResponse<string[]> = {
  status: 200,
  data: ['item1', 'item2'],
  message: 'success',
  timestamp: new Date()
};

5. Условные типы (Conditional Types)

// Если T это массив, вернуть элемент, иначе сам T
type Unwrap<T> = T extends Array<infer U> ? U : T;

type NumArray = Unwrap<number[]>;  // number
type StrType = Unwrap<string>;     // string

// Практический пример: может быть array или single value
type ApiData<T> = T extends Array<infer U>
  ? { items: U[]; total: number }
  : { item: T };

type UserList = ApiData<User[]>;   // { items: User[]; total: number }
type UserSingle = ApiData<User>;   // { item: User }

6. Mapped Types для генерации новых типов

type User = { name: string; age: number; email: string };

// Делаем все поля readonly
type ReadonlyUser = {
  readonly [K in keyof User]: User[K];
};

// Генерируем getters для каждого поля
type Getters<T> = {
  [K in keyof T as `get${Capitalize<string & K>}`]: () => T[K];
};

type UserGetters = Getters<User>;
// Результат:
// {
//   getName: () => string;
//   getAge: () => number;
//   getEmail: () => string;
// }

// Генерируем setters
type Setters<T> = {
  [K in keyof T as `set${Capitalize<string & K>}`]: (value: T[K]) => void;
};

7. Keyof для типобезопасности

type User = { name: string; age: number; email: string };

// Получить все ключи как тип
type UserKeys = keyof User; // 'name' | 'age' | 'email'

// Типобезопасная функция с ограничением ключей
function getValue<T, K extends keyof T>(obj: T, key: K): T[K] {
  return obj[key];
}

const user = { name: 'Alice', age: 30, email: 'alice@example.com' };

const name = getValue(user, 'name');      // ✅ string
const email = getValue(user, 'email');    // ✅ string
const invalid = getValue(user, 'phone');  // ❌ Ошибка компиляции!

Utility Types для готового расширения

type User = { name: string; age: number; email: string };

// Partial - все поля опциональны
type UserUpdate = Partial<User>;  // все поля ?

// Required - все поля обязательны
type UserRequired = Required<UserUpdate>;

// Omit - исключить определённые поля
type UserWithoutEmail = Omit<User, 'email'>; // { name; age }

// Pick - выбрать только эти поля
type UserPreview = Pick<User, 'name' | 'age'>;  // { name; age }

// Record - создать объект с определёнными ключами
type Permissions = Record<'read' | 'write' | 'delete', boolean>;
const perms: Permissions = {
  read: true,
  write: true,
  delete: false
};

Interface vs Type для расширения

// Interface можно расширить через extends
interface BaseUser {
  id: string;
  name: string;
}

interface AdminUser extends BaseUser {
  role: 'admin';
  permissions: string[];
}

// Type нужно использовать & для расширения
type BaseUserType = { id: string; name: string };
type AdminUserType = BaseUserType & { role: 'admin' };

// Оба способа работают одинаково
const admin: AdminUser = { id: '1', name: 'Alice', role: 'admin', permissions: [] };
const adminType: AdminUserType = { id: '1', name: 'Bob', role: 'admin' };

Практический пример: API Response расширение

// Базовый тип ответа API
type ApiResponse<T> = {
  success: boolean;
  data?: T;
  error?: string;
};

// Расширение для успешного ответа
type ApiSuccess<T> = ApiResponse<T> & {
  success: true;
  data: T;
  timestamp: Date;
};

// Расширение для ошибки
type ApiError = ApiResponse<never> & {
  success: false;
  error: string;
  errorCode: number;
};

// Union - может быть успех или ошибка
type ApiResult<T> = ApiSuccess<T> | ApiError;

// Использование
const success: ApiResult<User> = {
  success: true,
  data: { id: '1', name: 'Alice' },
  timestamp: new Date()
};

const error: ApiResult<User> = {
  success: false,
  error: 'User not found',
  errorCode: 404
};

Глубокое расширение (nested types)

type DeepPartial<T> = {
  [K in keyof T]?: T[K] extends object ? DeepPartial<T[K]> : T[K];
};

type User = {
  id: string;
  profile: {
    name: string;
    settings: {
      theme: string;
      notifications: boolean;
    };
  };
};

// Все уровни вложенности становятся опциональными
type UserUpdate = DeepPartial<User>;

const update: UserUpdate = {
  profile: {
    settings: {
      theme: 'dark'
      // notifications не обязательно
    }
    // name не обязательно
  }
  // id не обязательно
};

Чек-лист расширения типов

  1. Пересечение (&) для объединения двух типов
  2. Union (|) для альтернативных вариантов
  3. Generics для гибкости и переиспользования
  4. Mapped types для генерации новых типов из существующих
  5. Utility types (Partial, Pick, Omit, Required) для готовых решений
  6. Conditional types для логики на уровне типов
  7. Keyof для типобезопасности на ключах объектов

Итог: Type в TypeScript нельзя расширить как класс с extends, но можно создавать новые типы используя пересечение (&), union (|), generics, mapped types и utility types. Для частого расширения лучше использовать Interface с extends, или комбинировать несколько техник для максимальной гибкости.

Можно ли расширить type? | PrepBro