Hint
Cannot be instantiated directly — defines a contract for subclasses; can have concrete methods unlike interfaces
Abstract classes sit between interfaces (pure contracts) and concrete classes (fully implemented). They can have abstract methods (must be implemented by subclasses) and concrete methods (shared implementation).
abstract class Animal {
abstract sound(): string; // must be implemented by subclass
abstract name: string; // abstract property
// Concrete method — shared by all subclasses
describe(): string {
return `I am a ${this.name} and I say ${this.sound()}`;
}
}
// new Animal() — ❌ Error: cannot instantiate abstract class
class Dog extends Animal {
name = 'Dog'; // ✅ implements abstract property
sound(): string { // ✅ implements abstract method
return 'woof';
}
}
class Cat extends Animal {
name = 'Cat';
sound() { return 'meow'; }
}
const dog = new Dog();
console.log(dog.describe()); // "I am a Dog and I say woof"
Abstract class vs interface:
// Abstract class with shared constructor logic
abstract class BaseRepository<T> {
constructor(protected tableName: string) {}
abstract findById(id: number): Promise<T | null>;
abstract save(entity: T): Promise<T>;
// Shared utility
protected buildQuery(where: Partial<T>): string {
// generic SQL builder
return `SELECT * FROM ${this.tableName}`;
}
}