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

Для чего нужен Extend?

2.0 Middle🔥 152 комментариев
#Soft Skills и рабочие процессы

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

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

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

Для чего нужен Extend?

extend в TypeScript — это ключевое слово для наследования интерфейсов, типов и классов. Оно позволяет переиспользовать существующие типы и расширять их новыми свойствами, создавая иерархию типов.

Extend для интерфейсов

Наследование интерфейса:

interface Animal {
  name: string;
  age: number;
  makeSound(): void;
}

// Dog наследует все свойства Animal и добавляет свои
interface Dog extends Animal {
  breed: string;
  isGoodBoy: boolean;
}

const myDog: Dog = {
  name: 'Шарик',
  age: 5,
  breed: 'Лабрадор',
  isGoodBoy: true,
  makeSound() {
    console.log('Гав-гав!');
  }
};

Множественное наследование интерфейсов:

interface HasId {
  id: string;
}

interface HasTimestamp {
  createdAt: Date;
  updatedAt: Date;
}

interface User extends HasId, HasTimestamp {
  name: string;
  email: string;
}

// User должен иметь: id, createdAt, updatedAt, name, email
const user: User = {
  id: '123',
  name: 'Иван',
  email: 'ivan@example.com',
  createdAt: new Date(),
  updatedAt: new Date()
};

Extend для типов

Расширение типов:

type BaseUser = {
  id: string;
  name: string;
};

// AdminUser имеет все свойства BaseUser плюс новые
type AdminUser = BaseUser & {
  role: 'admin';
  permissions: string[];
};

const admin: AdminUser = {
  id: '1',
  name: 'Администратор',
  role: 'admin',
  permissions: ['read', 'write', 'delete']
};

Extend в классах (наследование)

Классический паттерн наследования:

class Animal {
  name: string;

  constructor(name: string) {
    this.name = name;
  }

  makeSound() {
    console.log('Животное издаёт звук');
  }
}

class Dog extends Animal {
  breed: string;

  constructor(name: string, breed: string) {
    super(name); // Вызываем конструктор родителя
    this.breed = breed;
  }

  // Переопределяем метод родителя (полиморфизм)
  makeSound() {
    console.log(`${this.name} лает: Гав-гав!`);
  }

  fetch() {
    console.log(`${this.name} принёс мяч`);
  }
}

const dog = new Dog('Шарик', 'Лабрадор');
dog.makeSound(); // Шарик лает: Гав-гав!
dog.fetch(); // Шарик принёс мяч

Extend для условных типов (Generic constraints)

Ограничение типа параметра:

// T должен иметь свойство 'id'
function getId<T extends { id: string }>(obj: T) {
  return obj.id;
}

getId({ id: '123', name: 'Иван' }); // OK
getId({ name: 'Иван' }); // ОШИБКА: нет свойства id

// T должен быть подтипом массива
function getLength<T extends any[]>(arr: T) {
  return arr.length;
}

getLength([1, 2, 3]); // OK
getLength('hello'); // ОШИБКА: string не массив

Extend с условными типами (Conditional Types)

Условная логика для типов:

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

type Str = Flatten<string[]>; // string
type Num = Flatten<number>; // number

// Проверка: является ли T функцией
type IsFunction<T> = T extends (...args: any[]) => any ? true : false;

type A = IsFunction<() => void>; // true
type B = IsFunction<string>; // false

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

// Базовый тип ответа от API
interface ApiResponse<T> {
  status: 'success' | 'error';
  data?: T;
  message: string;
}

// Специфичные типы ответов
interface User {
  id: string;
  name: string;
  email: string;
}

interface UserResponse extends ApiResponse<User> {
  // User response может иметь дополнительные поля
  timestamp: Date;
}

// Функция работает с любым ApiResponse
function handleResponse<T>(response: ApiResponse<T>) {
  if (response.status === 'success') {
    console.log('Данные:', response.data);
  } else {
    console.log('Ошибка:', response.message);
  }
}

// Использование
const userResponse: UserResponse = {
  status: 'success',
  data: {
    id: '1',
    name: 'Иван',
    email: 'ivan@example.com'
  },
  message: 'OK',
  timestamp: new Date()
};

handleResponse(userResponse);

Extend vs Intersection (&)

Интерфейсы используют extends:

interface A { x: number; }
interface B extends A { y: string; }

Типы используют пересечение (&):

type A = { x: number };
type B = A & { y: string };

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

Частая ошибка: Generic constraints

// НЕПРАВИЛЬНО: T может быть чем угодно
function printId<T>(obj: T) {
  console.log(obj.id); // ОШИБКА: у T может не быть id
}

// ПРАВИЛЬНО: T должен иметь id
function printId<T extends { id: string }>(obj: T) {
  console.log(obj.id); // OK
}

printId({ id: '123' }); // OK
printId({ name: 'Иван' }); // ОШИБКА: нет id

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

  • extend расширяет интерфейсы и типы — добавляет новые свойства
  • Используй extends для наследования классов — переиспользование кода
  • Generic constraints с extends — ограничение типов параметров
  • Conditional types — условная логика для типов
  • Помни про множественное наследование — интерфейсы могут расширять несколько других

Правильное использование extends критично для типобезопасности и переиспользования кода в TypeScript.

Для чего нужен Extend? | PrepBro