← Back to TypeScript Course | Chapter 6: Classes | Lesson 7 of 9

Abstract Classes

An abstract class is a base class that cannot be instantiated directly. It can define shared behavior and require derived classes to implement abstract methods.
Syntax
typescript
abstract class ClassName {
  abstract method(): returnType;
}
class Concrete extends ClassName {
  method(): returnType {
    // implementation
  }
}

Creating an Abstract Class

An abstract class is declared with the abstract keyword and is designed to be extended, not instantiated directly — attempting new on an abstract class is a compile-time error.

Example: Creating an Abstract Class

typescript
abstract class Shape {
  abstract area(): number;
}
// new Shape(); // rejected: cannot instantiate an abstract class
class Circle extends Shape {
  constructor(public radius: number) { super(); }
  area() { return 3.14 * this.radius ** 2; }
}
console.log(new Circle(5).area());

Abstract Methods

An abstract method has a declaration but no implementation in the abstract base class; every concrete subclass is required to supply its own implementation, which the compiler enforces.

Example: Abstract Methods

typescript
abstract class Animal {
  abstract makeSound(): string;
}
class Dog extends Animal {
  makeSound() { return "Woof"; }
}
console.log(new Dog().makeSound());

Concrete Methods in Abstract Classes

Abstract classes can also contain normal concrete methods alongside abstract ones, letting shared, already-working logic live in the base class while only the parts that genuinely vary are left for subclasses to fill in.

Example: Concrete Methods in Abstract Classes

typescript
abstract class Animal {
  abstract makeSound(): string;
  describe() {
    return `This animal says: ${this.makeSound()}`;
  }
}
class Cat extends Animal {
  makeSound() { return "Meow"; }
}
console.log(new Cat().describe());

Abstract Classes and Polymorphism

An abstract class can be used as a type for references to different concrete subclasses, so code written against the abstract type works uniformly no matter which specific subclass is passed in at runtime.

Example: Abstract Classes and Polymorphism

typescript
abstract class Shape {
  abstract area(): number;
}
class Square extends Shape {
  constructor(public side: number) { super(); }
  area() { return this.side ** 2; }
}
class Circle extends Shape {
  constructor(public radius: number) { super(); }
  area() { return 3.14 * this.radius ** 2; }
}
const shapes: Shape[] = [new Square(4), new Circle(2)];
shapes.forEach((s) => console.log(s.area()));

When to Use Abstract Classes

Abstract classes are useful when related classes share real implementation but must also provide their own version of at least one method — if there's nothing shared at all, a plain interface is usually the better fit.

Example: When to Use Abstract Classes

typescript
abstract class Employee {
  constructor(public name: string) {}
  abstract calculateSalary(): number;
  printPaycheck() {
    console.log(`${this.name}: $${this.calculateSalary()}`);
  }
}
class Manager extends Employee {
  calculateSalary() { return 5000; }
}
new Manager("Alia").printPaycheck();
Related Topics
{# common_mistakes/chapter_summary/browser_support: on Hindi pages the view already swaps in the hi_ translation fields (or blanks these out if untranslated), so this renders correctly for both languages without a lang_code check here. #}
Common Mistakes
  1. Trying new Shape() on an abstract class, which is an error because abstract classes can only be extended.
  2. Forgetting to implement an abstract method in a concrete subclass, which makes the subclass itself fail to compile.
  3. Writing a body for an abstract method (abstract area() { }), when abstract methods must have only a signature.

Login to run this code

C/C++/Java/PHP execution requires a free account. Your code is saved — you'll land right back in the editor after logging in.