Polymorphism in TypeScript: Leveraging Interfaces and Abstract Classes for Flexible Code Design

Introduction

Greetings! In today's lesson, we'll unravel the concept of polymorphism in TypeScript's Object-Oriented Programming (OOP). Grasping polymorphism enables us to use a single entity (a method, class, or interface) to represent different types in various scenarios. With TypeScript's static typing, we can efficiently manage polymorphic behaviors and enforce type safety for more robust applications. Let's proceed.

Polymorphism: A Powerful OOP Principle

Polymorphism, a pillar of OOP, allows one object to embody multiple forms. Visualize a button in software; depending on its type (for instance, a submit button or a radio button), the action resulting from pressing it varies. This dynamic encapsulates the spirit of polymorphism!

Polymorphism in OOP can generally be categorized into two types:

  • Static Polymorphism (Compile-time): This occurs when the method to call is determined at compile-time, typically through method overloading (having multiple methods with the same name but different parameters). Although TypeScript doesn’t support traditional method overloading as in some other languages, we can achieve similar functionality through function overloading.
  • Dynamic Polymorphism (Runtime): This is achieved through method overriding, where a subclass provides a specific implementation of a method defined in its superclass or interface. In TypeScript, this is commonly implemented using interfaces or abstract classes.

Seeing Polymorphism in Action

Let's observe polymorphism in action within a simple application involving shapes. The base Shape class has an area method, which calculates the area for shapes. This method is uniquely implemented in the subclasses Rectangle and Circle.

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

class Rectangle extends Shape {
    private length: number;
    private width: number;

    constructor(length: number, width: number) {
        super();
        this.length = length;
        this.width = width;
    }

    area(): number {  // calculate rectangle area
        return this.length * this.width;
    }
}

class Circle extends Shape {
    private radius: number;

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

    area(): number {  // calculate circle area
        return 3.14 * this.radius * this.radius;
    }
}

const rectangle = new Rectangle(2, 3);
console.log(rectangle.area()); // Prints: 6

const circle = new Circle(5);
console.log(circle.area()); // Prints: 78.5

Here, polymorphism shines as the area function takes on multiple forms and behaves differently depending on whether it's part of a Rectangle or a Circle.

Sign up

Join the 1M+ learners on CodeSignal

Be a part of our community of 1M+ users who develop and demonstrate their skills on CodeSignal