Magento 2 Experten — Hyvä Theme, Tailwind CSS & SEO aus einer Hand ›

Abstract Classes and Implementing Interfaces in TypeScript

Abstract Classes and Implementing Interfaces

~15 Min. Lesezeit Zuletzt aktualisiert am August 8, 2026

Our Medium class from chapter 13 can currently be instantiated DIRECTLY (new Medium(...)) – conceptually nonsensical, since a "medium" by itself is abstract; in the real world there are only CONCRETE media (books, audiobooks). Abstract classes enforce exactly that.

Marking Medium as abstract

export abstract class Medium {
  constructor(
    public readonly isbn: string,
    public title: string,
    // ... remaining properties as in chapter 13
  ) {}

  abstract description(): string; // NO implementation - only the signature
}

new Medium('123', 'Test'); // error - abstract classes CANNOT be instantiated directly

abstract class forbids new Medium(...) – the class exists ONLY as a base for inheritance. abstract description(): string (with no function body!) FORCES every derived, CONCRETE class to implement this method ITSELF.

export class Book extends Medium {
  // description() MUST be implemented - otherwise error:
  // "Non-abstract class 'Book' does not implement inherited abstract member 'description'"
  description(): string {
    return `${this.title} (Book)`;
  }
}

Achtung: The difference from a normal (non-abstract) base class with a "default" implementation: with abstract, the compiler ACTIVELY checks that EVERY derived class provides the method itself – in chapter 13's Medium.description() with a real implementation, overriding it in Audiobook was only OPTIONAL (override), here it's MANDATORY.

Implementing interfaces with implements

A RELATED but different concept: a class can promise to FULFILL an interface, with implements:

src/models/Borrowable.ts
export interface Borrowable {
  isAvailable(): boolean;
  checkOut(): void;
  returnItem(): void;
}
import { Borrowable } from './Borrowable.js';

export abstract class Medium implements Borrowable {
  status: 'available' | 'borrowed' = 'available';

  isAvailable(): boolean {
    return this.status === 'available';
  }

  checkOut(): void {
    this.status = 'borrowed';
  }

  returnItem(): void {
    this.status = 'available';
  }

  abstract description(): string;
}

implements Borrowable OBLIGATES Medium to provide ALL of the interface's methods – the compiler checks this IMMEDIATELY at the class declaration, not somewhere later in the code.

The difference: abstract class vs. interface

ToolProperty
abstract classCan bring PARTIAL implementation (finished methods AND abstract placeholders mixed) – a class can only inherit from ONE abstract class.
interface with implementsA PURE contract with no implementation at all – a class can implement ANY NUMBER of interfaces at once.
// A class can inherit from ONLY ONE base class:
class Book extends Medium { /* ... */ }

// BUT can implement any number of interfaces at once:
interface Reservable {
  reserve(): void;
}

class Book extends Medium implements Borrowable, Reservable {
  reserve(): void {
    this.status = 'reserved';
  }
}

Tipp: Rule of thumb: abstract class when derived types SHOULD share COMMON code (like isAvailable() here, identical for Book and Audiobook). interface + implements when you only want to enforce a CONTRACT ("this class MUST have these methods"), WITHOUT prescribing a shared implementation – especially useful when a class should have several INDEPENDENT "capabilities" at once.