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

Когда применяется ключевое слово Extends?

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

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

🐱
deepseek-v3.2PrepBro AI4 апр. 2026 г.(ред.)

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

Применение ключевого слова extends в JavaScript/TypeScript

Ключевое слово extends является фундаментальной частью объектно-ориентированного программирования в JavaScript/TypeScript и применяется в нескольких ключевых контекстах для реализации механизма наследования.

Основные случаи применения

1. Наследование классов в JavaScript/TypeScript

extends используется для создания дочернего класса, который наследует свойства и методы родительского класса:

// Базовый (родительский) класс
class Animal {
  constructor(name) {
    this.name = name;
  }
  
  speak() {
    console.log(`${this.name} издает звук`);
  }
}

// Дочерний класс, наследуемый от Animal
class Dog extends Animal {
  constructor(name, breed) {
    super(name); // Вызов конструктора родительского класса
    this.breed = breed;
  }
  
  speak() {
    console.log(`${this.name} лает: Гав-гав!`);
  }
  
  fetch() {
    console.log(`${this.name} приносит мяч`);
  }
}

const myDog = new Dog('Бобик', 'Овчарка');
myDog.speak(); // "Бобик лает: Гав-гав!"
myDog.fetch(); // "Бобик приносит мяч"

2. Наследование встроенных классов

extends позволяет создавать кастомные классы на основе встроенных:

class CustomArray extends Array {
  // Добавляем пользовательский метод
  last() {
    return this.length > 0 ? this[this.length - 1] : undefined;
  }
  
  // Переопределяем существующий метод
  push(...items) {
    console.log(`Добавлено ${items.length} элементов`);
    return super.push(...items);
  }
}

const arr = new CustomArray(1, 2, 3);
console.log(arr.last()); // 3
arr.push(4, 5); // "Добавлено 2 элементов"

3. TypeScript: расширение интерфейсов и типов

В TypeScript extends используется для создания производных интерфейсов:

// Базовый интерфейс
interface Vehicle {
  brand: string;
  startEngine(): void;
}

// Расширенный интерфейс
interface Car extends Vehicle {
  doors: number;
  drive(): void;
}

// Реализация расширенного интерфейса
class Sedan implements Car {
  brand: string;
  doors: number;
  
  constructor(brand: string, doors: number) {
    this.brand = brand;
    this.doors = doors;
  }
  
  startEngine() {
    console.log('Двигатель запущен');
  }
  
  drive() {
    console.log('Машина едет');
  }
}

4. TypeScript: Generic Constraints (ограничения дженериков)

extends устанавливает ограничения для универсальных типов:

// Ограничение: T должен иметь свойство length
function logLength<T extends { length: number }>(item: T): void {
  console.log(`Длина: ${item.length}`);
}

logLength([1, 2, 3]); // Работает
logLength('строка'); // Работает
logLength({ length: 5 }); // Работает
// logLength(42); // Ошибка: number не имеет свойства length

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

В продвинутых типах TypeScript extends используется для условной логики типов:

// Условный тип
type IsString<T> = T extends string ? true : false;

type A = IsString<'hello'>; // true
type B = IsString<number>; // false

// Использование в утилитарных типах
type ExtractString<T> = T extends string ? T : never;
type Result = ExtractString<string | number | boolean>; // string

Ключевые принципы и особенности

Принцип подстановки Барбары Лисков: Объекты дочернего класса должны быть заменяемы объектами родительского класса без нарушения работы программы.

Цепочка прототипов: При использовании extends создается иерархия прототипов:

class A {}
class B extends A {}
class C extends B {}

console.log(C.prototype.__proto__ === B.prototype); // true
console.log(B.prototype.__proto__ === A.prototype); // true
console.log(A.prototype.__proto__ === Object.prototype); // true

Модификаторы доступа: В TypeScript extends взаимодействует с модификаторами:

  • public - наследуются везде
  • protected - наследуются в иерархии
  • private - не наследуются

Практические рекомендации

  1. Используйте наследование осторожно: Предпочитайте композицию над наследованием там, где это уместно
  2. Следуйте принципу единой ответственности: Класс должен иметь только одну причину для изменения
  3. Избегайте глубоких иерархий: Глубокие цепочки наследования усложняют понимание кода
  4. Используйте абстрактные классы (TypeScript) для определения общего поведения:
abstract class Shape {
  abstract getArea(): number;
  
  display() {
    console.log(`Площадь: ${this.getArea()}`);
  }
}

class Circle extends Shape {
  constructor(private radius: number) {
    super();
  }
  
  getArea(): number {
    return Math.PI * this.radius ** 2;
  }
}

Ключевое слово extends является мощным инструментом для создания иерархий типов и классов, но требует осмысленного применения с учетом принципов SOLID и конкретных требований проекта.