Abstract Classes: The Blueprint for Inheritance

Learn how to create robust object-oriented structures using abstract classes in TypeScript. Explore key concepts, syntax, and practical examples to master the art of abstract class implementation and inheritance.



Abstract Class

In TypeScript, abstract classes serve as a foundation for building related classes through inheritance. These classes act as blueprints, defining common properties and methods to be shared by derived classes, while also enforcing specific behaviors through abstract methods.

Key Concepts

  • Declaration: We use the abstract keyword to define an abstract class.
  • Inheritance: Other classes, known as derived classes, can inherit from the abstract class.
  • Instantiation Restriction: You cannot directly create instances (objects) of an abstract class.
  • Abstract Methods: Abstract methods lack implementation within the abstract class itself. Derived classes are obligated to provide concrete implementations for these methods.

Example: Defining an Abstract Class Person

Syntax

abstract class Person {

  name: string;

  constructor(name: string) {
    this.name = name;
  }

  display(): void {
    console.log(this.name);
  }

  abstract find(name: string): Person; // Abstract method
}
Output

Person is an abstract class.
It has a property 'name' and two methods: 'display' and 'find'.
'display' is a regular method with an implementation for logging the name.
'find' is declared as an abstract method. It defines the method signature but lacks an actual implementation.

Derived Class: Employee Inheriting from Person

Syntax

class Employee extends Person {

  empCode: number;

  constructor(name: string, code: number) {
    super(name); // Call super constructor
    this.empCode = code;
  }

  find(name: string): Person {
    // Implement logic to find an employee (e.g., using a database)
    return new Employee(name, 123); // Example return value
  }
}
Output

Employee extends the Person class, inheriting properties and methods.
The Employee constructor calls the super constructor using super(name) to initialize the inherited name property.
find is implemented in Employee, providing the specific logic for finding an employee.

Key Points

  • Abstract classes define the contract (properties and methods) that derived classes must adhere to.
  • Derived classes provide concrete implementations for abstract methods inherited from the abstract class.
  • Abstract methods enforce consistency in behavior across derived classes.

Using the Abstract Class

While you cannot directly create an instance of Person, you can create objects of Employee:

Syntax

let emp: Person = new Employee("Alice", 101); // Allowed, using derived class
emp.display(); // Calls inherited method

// let emp2: Person = new Person("Bob", 102); // Not allowed, cannot create instance of abstract class
Output

Employee instance created with name 'Alice' and empCode 101.
Attempt to create a Person instance directly is not allowed.

Abstract Properties

Abstract classes can also have abstract properties without initializers:

Syntax

abstract class Shape {
  abstract area: number;
}

class Square extends Shape {
  sideLength: number;

  constructor(sideLength: number) {
    super();
    this.sideLength = sideLength;
  }

  get area(): number {
    return this.sideLength * this.sideLength;
  }
}
Output

Shape class has an abstract property 'area'.
Square class implements the 'area' property by calculating the area of a square.

Benefits of Abstract Classes

  • Improved Code Reusability: Abstract classes promote code reuse by encapsulating common functionalities.
  • Enforced Consistency: Derived classes must implement abstract methods, ensuring consistent behavior.
  • Clearer Class Hierarchies: Abstract classes define the relationships between classes.

When to Use Abstract Classes

  • When you want to define a common structure and behavior for a group of related classes.
  • When you want to enforce specific functionality in derived classes.
  • When creating a base class for inheritance hierarchies.

By understanding abstract classes, you can leverage them to create well-structured, reusable, and maintainable object-oriented code in TypeScript.