TYPESCRIPT

TypeScript Abstract Class: Syntax, Usage, and Examples

A TypeScript abstract class is a special type of class that serves as a blueprint for other classes. You cannot instantiate an abstract class directly; instead, you must extend it to create a concrete implementation. Abstract classes help enforce structure in object-oriented programming by defining common methods and properties for subclasses.

How to Use an Abstract Class in TypeScript

To define an abstract class, use the abstract keyword before the class declaration. Any method inside an abstract class that lacks an implementation must also be marked as abstract.

abstract class Animal {
  abstract makeSound(): void; // Must be implemented by subclasses

  move(): void {
    console.log("Moving...");
  }
}

class Dog extends Animal {
  makeSound(): void {
    console.log("Bark!");
  }
}

const myDog = new Dog();
myDog.makeSound(); // Output: Bark!
myDog.move(); // Output: Moving...
  • The Animal class is abstract, meaning you cannot create an instance of it.
  • makeSound() is an abstract method, so every subclass must implement it.
  • move() is a regular method that all subclasses inherit automatically.

When to Use an Abstract Class in TypeScript

Enforcing Structure in a Class Hierarchy

Abstract classes help define a common structure for subclasses, ensuring that specific methods exist across all implementations.

abstract class Employee {
  constructor(public name: string) {}

  abstract getRole(): string;
}

class Manager extends Employee {
  getRole(): string {
    return "Manager";
  }
}

class Developer extends Employee {
  getRole(): string {
    return "Developer";
  }
}

const manager = new Manager("Alice");
console.log(manager.getRole()); // Output: Manager

const developer = new Developer("Bob");
console.log(developer.getRole()); // Output: Developer

Preventing Direct Instantiation

Abstract classes act as templates, preventing direct instantiation while still allowing shared functionality.

abstract class Shape {
  abstract getArea(): number;
}

class Circle extends Shape {
  constructor(public radius: number) {
    super();
  }

  getArea(): number {
    return Math.PI * this.radius * this.radius;
  }
}

const circle = new Circle(5);
console.log(circle.getArea()); // Output: 78.54

Providing Default Behavior

Abstract classes can include methods with implementations to provide common behavior while allowing subclasses to override them if needed.

abstract class Vehicle {
  start(): void {
    console.log("Starting the engine...");
  }

  abstract drive(): void;
}

class Car extends Vehicle {
  drive(): void {
    console.log("Driving a car...");
  }
}

const car = new Car();
car.start(); // Output: Starting the engine...
car.drive(); // Output: Driving a car...

Examples of Abstract Classes in TypeScript

Using an Abstract Class Constructor

A constructor in an abstract class allows you to initialize shared properties.

abstract class Animal {
  constructor(public name: string) {}

  abstract makeSound(): void;
}

class Cat extends Animal {
  makeSound(): void {
    console.log(`${this.name} says Meow!`);
  }
}

const myCat = new Cat("Whiskers");
myCat.makeSound(); // Output: Whiskers says Meow!

Abstract Classes with Optional Methods

Abstract classes can include optional methods that subclasses may implement if needed.

abstract class Logger {
  abstract logMessage(message: string): void;

  logWarning?(message: string): void;
}

class ConsoleLogger extends Logger {
  logMessage(message: string): void {
    console.log(`Log: ${message}`);
  }

  logWarning(message: string): void {
    console.warn(`Warning: ${message}`);
  }
}

const logger = new ConsoleLogger();
logger.logMessage("Hello!"); // Output: Log: Hello!
logger.logWarning("Be careful!"); // Output: Warning: Be careful!

Overriding Abstract Methods

A subclass can override an inherited method from the abstract class.

abstract class Worker {
  abstract work(): void;

  takeBreak(): void {
    console.log("Taking a short break.");
  }
}

class Programmer extends Worker {
  work(): void {
    console.log("Writing code...");
  }

  takeBreak(): void {
    console.log("Stretching and taking a coffee break.");
  }
}

const dev = new Programmer();
dev.work(); // Output: Writing code...
dev.takeBreak(); // Output: Stretching and taking a coffee break.

Learn More About Abstract Classes in TypeScript

Abstract Class vs Interface

While both abstract classes and interfaces define structures, they serve different purposes.

  • Abstract classes can contain method implementations and state (properties).
  • Interfaces only define method signatures and cannot include implementations.

interface Animal {
  makeSound(): void;
}

class Bird implements Animal {
  makeSound(): void {
    console.log("Chirp!");
  }
}

Use an interface when you only need structure, and use an abstract class when you need to include common behavior.

Extending an Abstract Class

A subclass can extend an abstract class while maintaining its abstract status.

abstract class Person {
  abstract introduce(): void;
}

abstract class Employee extends Person {
  abstract getJobTitle(): string;
}

class Engineer extends Employee {
  introduce(): void {
    console.log("Hi, I'm an Engineer.");
  }

  getJobTitle(): string {
    return "Software Engineer";
  }
}

const engineer = new Engineer();
engineer.introduce(); // Output: Hi, I'm an Engineer.
console.log(engineer.getJobTitle()); // Output: Software Engineer

Static Methods in Abstract Classes

You can define static methods inside abstract classes to enforce shared logic at the class level.

abstract class MathUtils {
  static square(n: number): number {
    return n * n;
  }
}

console.log(MathUtils.square(4)); // Output: 16

Exporting Abstract Classes

You can export an abstract class to use it across multiple files.

export abstract class Animal {
  abstract makeSound(): void;
}

Using Abstract Classes with Generics

Generics make abstract classes more flexible for different types.

abstract class Repository<T> {
  abstract getAll(): T[];
}

class UserRepository extends Repository<string> {
  getAll(): string[] {
    return ["Alice", "Bob", "Charlie"];
  }
}

const userRepo = new UserRepository();
console.log(userRepo.getAll()); // Output: ["Alice", "Bob", "Charlie"]

Implementing Abstract Class Mixins

Mixins allow combining functionality from multiple abstract classes.

type Constructor<T = {}> = new (...args: any[]) => T;

function Serializable<TBase extends Constructor>(Base: TBase) {
  return class extends Base {
    serialize() {
      return JSON.stringify(this);
    }
  };
}

abstract class Model {
  abstract getId(): number;
}

class User extends Serializable(Model) {
  constructor(public id: number, public name: string) {
    super();
  }

  getId(): number {
    return this.id;
  }
}

const user = new User(1, "Alice");
console.log(user.serialize()); // Output: {"id":1,"name":"Alice"}

TypeScript abstract classes help enforce structure while allowing shared functionality across multiple subclasses. They provide flexibility by combining default behaviors with abstract methods that subclasses must implement.

Learn TypeScript for Free
Start learning now
button icon
To advance beyond this tutorial and learn TypeScript by doing, try the interactive experience of Mimo. Whether you're starting from scratch or brushing up your coding skills, Mimo helps you take your coding journey above and beyond.

Sign up or download Mimo from the App Store or Google Play to enhance your programming skills and prepare for a career in tech.

You can code, too.

© 2025 Mimo GmbH