Skip to main content
Практика завдань

Абстрактні класи в TypeScript

Що таке абстрактні класи?

Абстрактний клас — це клас, який не може бути інстанційований безпосередньо — він слугує як базовий клас для інших класів. Він може містити як реалізовані методи, так і абстрактні методи (методи без реалізації, які підкласи повинні реалізувати).


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

typescript
abstract class Shape { // Абстрактний метод — без реалізації, повинен бути перевизначений abstract area(): number; abstract perimeter(): number; // Конкретний метод — має реалізацію, успадковується підкласами describe(): string { return `Area: ${this.area()}, Perimeter: ${this.perimeter()}`; } } // ❌ Не можна інстанціювати абстрактний клас const shape = new Shape(); // Помилка! // ✅ Потрібно розширити та реалізувати абстрактні методи class Circle extends Shape { constructor(private radius: number) { super(); } area(): number { return Math.PI * this.radius ** 2; } perimeter(): number { return 2 * Math.PI * this.radius; } } const circle = new Circle(5); circle.area(); // 78.54 circle.describe(); // "Area: 78.54, Perimeter: 31.42"

Абстрактні класи vs Інтерфейси

ОсобливістьАбстрактний класІнтерфейс
РеалізаціяМоже мати реалізовані методиБез реалізації
КонструкторТакНі
Модифікатори доступуТак (public, private, protected)Ні
Властивості з значеннямиТакНі
Багатократне успадкуванняНі (одне розширення)Так (багато реалізацій)
Існування під час виконанняТак (транспілюється в клас JS)Ні (знищується під час компіляції)
typescript
// Інтерфейс — чистий контракт interface Printable { print(): void; } // Абстрактний клас — контракт + спільна логіка abstract class BaseRepository<T> { protected items: T[] = []; abstract findById(id: string): T | undefined; getAll(): T[] { return [...this.items]; } count(): number { return this.items.length; } }

Практичні випадки використання

Шаблон репозиторію

typescript
abstract class BaseRepository<T extends { id: string }> { protected abstract collection: string; abstract save(entity: T): Promise<T>; abstract delete(id: string): Promise<void>; async findById(id: string): Promise<T | null> { // Спільна реалізація з використанням this.collection const response = await fetch(`/api/${this.collection}/${id}`); return response.json(); } } class UserRepository extends BaseRepository<User> { protected collection = "users"; async save(user: User): Promise<User> { // Логіка збереження, специфічна для користувача return user; } async delete(id: string): Promise<void> { // Логіка видалення, специфічна для користувача } }

Абстрактні властивості

typescript
abstract class Component { abstract readonly tagName: string; abstract render(): string; mount(container: HTMLElement): void { container.innerHTML = this.render(); } } class Button extends Component { readonly tagName = "button"; render(): string { return `<${this.tagName}>Click me</${this.tagName}>`; } }

Коли використовувати абстрактні класи

  • Спільна поведінка — загальна логіка, яка потрібна всім підкласам
  • Шаблонний метод — визначити структуру алгоритму, дозволити підкласам заповнити кроки
  • Стан з поведінкою — коли вам потрібні як властивості, так і методи
  • Єдина ієрархія — коли класи природно формують дерево

Коли використовувати інтерфейси замість цього

  • Багато контрактів — класу потрібно реалізувати кілька контрактів
  • Чиста форма — просто визначення структури даних
  • Без спільної логіки — немає спільної реалізації для поділу
  • Розв'язування — слабке зв'язування між модулями

Важливо:

Використовуйте абстрактні класи, коли підкласи ділять загальну реалізаційну логіку. Використовуйте інтерфейси, коли вам потрібно лише визначити контракт (форму). На практиці ви часто будете використовувати обидва: абстрактні класи для ієрархій успадкування та інтерфейси для контрактів можливостей.

Коротка відповідь

Для співбесіди
Premium

Коротка відповідь допоможе вам впевнено відповідати на цю тему під час співбесіди.

Дочитали статтю?
Практика завдань