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 directlyabstract 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:
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
| Tool | Property |
|---|---|
abstract class | Can bring PARTIAL implementation (finished methods AND abstract placeholders mixed) – a class can only inherit from ONE abstract class. |
interface with implements | A 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.