Menu

Абстрактные классы в TypeScript: методы, свойства, интерфейс

Абстрактный класс в TypeScript это базовый класс, экземпляр которого нельзя создать и который может оставлять методы для реализации в подклассах. Абстрактные методы и свойства, шаблонный метод, абстрактные типы конструкторов и когда интерфейс подходит лучше.

На этой странице есть исполняемые редакторы: меняйте, запускайте и сразу видите результат.

Абстрактный класс это базовый класс с пометкой abstract. Его нельзя создать через new, и он может объявлять абстрактные (abstract) методы без тела, которые обязан реализовать каждый подкласс.

Shape делится методом describe() со всеми подклассами и заставляет каждый из них предоставить area(). Тип Shape[] вмещает любую смесь подклассов.

Экземпляр абстрактного класса создать нельзя

Вызов new для абстрактного класса это ошибка компиляции, потому что у объекта были бы методы без реализации:

index.ts(6,11): error TS2511: Cannot create an instance of an abstract class.

Это правило времени компиляции. abstract стирается из вывода, поэтому сгенерированный Shape это обычный класс JavaScript, и нетипизированный код на JavaScript всё равно может вызвать new Shape(). Если библиотеке нужна защита во время выполнения, конструктор может проверять new.target:

Абстрактные методы и свойства

Подкласс должен реализовать каждый абстрактный член или сам быть абстрактным. Пропуск одного члена это ошибка TS2515, которая выглядит как Non-abstract class 'Square' does not implement inherited abstract member area from class 'Shape', или TS2654 со списком всех, если пропущено несколько.

Абстрактными могут быть поля, геттеры и методы, и они могут быть protected:

У абстрактного метода нет тела; если его написать, будет ошибка TS1245 (Method 'x' cannot have an implementation because it is marked abstract). Абстрактному классу вообще не обязательно иметь абстрактные члены: abstract сам по себе просто запрещает прямое создание экземпляра.

Шаблонный метод

Самая частая причина писать абстрактный класс: базовый класс задаёт порядок шагов, а каждый подкласс заполняет шаги, которые различаются.

Абстрактные классы могут быть обобщёнными, как здесь, чтобы каждый подкласс выбирал тип записи.

Абстрактные типы конструкторов

typeof Shape для абстрактного класса это конструктор, который нельзя вызывать, поэтому параметр с типом typeof Shape не может выполнить new c(). Если функция должна принимать абстрактные классы (например, чтобы регистрировать их или проверять instanceof), типизируйте параметр абстрактной сигнатурой конструктора:

abstract new принимает и абстрактные, и конкретные классы; обычный new принимает только конкретные.

Абстрактный класс или интерфейс

Абстрактный классИнтерфейс
Содержит кодда: методы, инициализаторы полей, конструкторнет, только типы
Существует во время выполненияданет, стирается
instanceofработаетневозможен
Класс может использоватьодин (extends)много (implements)
Модификаторы доступаprivate, protected, publicтолько публичные члены
Тип для обычных объектовтолько если нет членов private/protected, и тогда instanceof не срабатываетда, любое значение с такой структурой

Практическое правило: интерфейс, когда нужно только описать структуру (самый частый случай, и он работает и для объектных литералов), абстрактный класс, когда подклассы делят настоящую реализацию и вы хотите, чтобы компилятор заставил их заполнить пробелы. Их удобно сочетать: интерфейс для публичного контракта, абстрактный класс как удобная основа, которая его реализует. Другое частое сравнение см. в разделе interface или type.

Частые ошибки

  • Абстрактный класс там, где хватило бы интерфейса. Если в базовом классе нет кода, это интерфейс с лишним весом во время выполнения и ограничением одиночного наследования.
  • Попытка написать static abstract. Это запрещено. Статические члены не могут быть абстрактными.
  • Ожидание ошибки во время выполнения от new для абстрактного класса. Её нет, если вы не напишете проверку new.target.
  • Вызов абстрактного метода из базового конструктора. Он выполняется до инициализации полей подкласса, поэтому реализация видит их как undefined.

Часто задаваемые вопросы

Что такое абстрактный класс в TypeScript?

Класс с пометкой abstract, который нельзя создать через new и который может объявлять члены abstract без реализации. Подклассы обязаны реализовать каждый абстрактный член. Это способ одновременно делиться кодом и требовать определённую структуру.

Чем абстрактный класс отличается от интерфейса в TypeScript?

Абстрактный класс может содержать настоящий код (реализованные методы, поля с начальными значениями, конструктор) и существует во время выполнения, поэтому instanceof с ним работает; класс может расширять только один такой класс. У интерфейса нет кода, он стирается при компиляции, и класс может реализовать много интерфейсов. Используйте абстрактный класс, чтобы делиться поведением, а интерфейс, чтобы описать структуру.

Может ли у абстрактного класса быть конструктор в TypeScript?

Да. Он выполняется, когда подкласс вызывает super(...). Но вызвать new для самого абстрактного класса по-прежнему нельзя (TS2511: Cannot create an instance of an abstract class).

Могут ли статические методы быть абстрактными в TypeScript?

Нет. abstract применяется только к членам экземпляра; static abstract это ошибка TS1243 ('static' modifier cannot be used with 'abstract' modifier). Чтобы потребовать статическую структуру, типизируйте конструктор интерфейсом, включающим статический член, и проверяйте класс по нему.

Соблюдается ли абстрактность класса во время выполнения?

Нет. abstract стирается, поэтому скомпилированный класс это обычный JavaScript, и код на JavaScript всё равно может вызвать для него new. Если это важно, проверяйте new.target в конструкторе и выбрасывайте ошибку.

Coddy programming languages illustration

Учитесь программировать с Coddy

НАЧАТЬ