Interface Segregation Principle in TypeScript

The interface Segregation Principle is one of the fundamental principles of the SOLID principle. It says to keep the interface as small as possible. In the words of Uncle Bob, keep interfaces small so that classes don’t end up depending on things they don’t need.

A client ( class) should not be forced to implement an interface it does not use, or a client( class) should not depend on methods they do not use.

To understand the problem, say there is an interface IVehicle with two methods.

export interface IVehicle{
    drive():void; 
    fly():void; 
}

Two classes, Car and Plane, implement the IVehicle interface.  The Car class uses the interface as shown in the next code block,

import { IVehicle } from "./interface/vehicle.interface";

export class Car implements IVehicle{

    drive(): void {
        console.log('can drive');
    }
    fly(): void {
        throw new Error('Method is not implemented.'); 
    }
}

Since a car cannot fly, the Car class is forced to implement the fly method, and for that, in the TypeScript, we are throwing an error with the message that Method is not implemented.

The Plane class uses the interface as shown in the next code block,

import { IVehicle } from "./interface/vehicle.interface";

export class Plane implements IVehicle{

    drive(): void {
        throw new Error("Method is not implemented.");
    }

    fly(): void {
        console.log('you can fly a plane')
    }

}

Since a plane cannot drive, the Plane class is forced to implement the drive method, and for that, in the TypeScript, we are throwing an error with the message that Method is not implemented.

The above implementation violates SOLID’s ISP principle.  

To adhere to the Interface Segregation Principle, refactor the IVehicle interface in two different interfaces.

  • ICar interface
  • IPlane interface
export interface ICar{
    drive():void;  
}

The ICar interface contains only the drive function, and the IPlane interface includes only the fly function.

export interface IPlane{
    fly():void; 
}

The Car and Plane classes implement these two interfaces ICar and IPlane, respectively.

import { ICar } from "./interface/car.interface";

export class Car implements ICar{

    drive(): void {
        console.log('you can drive a car');
    }
}

And the Plane class implements the IPlane interface,

import { IPlane } from "./interface/plane.interface";

export class Plane implements IPlane{

    fly(): void {
        console.log('you can fly a plane')
    }

}

In this implementation,

  • Interface is small
  • The class that implements the interface is not forced to implement any method it does not need.
  • The class is not violating the Single Responsibility Principle.

After refactoring to the Interface Segregation Principle, interfaces and classes are in a one-to-one relationship and adhere to the Single Responsibility Principle.

If there is a super vehicle that flies and drives both, that can implement both interfaces.

import { ICar } from "./interface/car.interface";
import { IPlane } from "./interface/plane.interface";

export class SuperVehicle implements ICar,IPlane {

    fly(): void {
        console.log('can fly');
    }
    drive(): void {
        console.log('can drive'); 
    }

}

In the above implementation,  as we have created specific interfaces, classes never implement the interfaces it does not need.

I strongly recommend using SOLID principles while working with TypeScript in Angular or node applications. I hope you find this post helpful. Thanks for reading.

One response to “Interface Segregation Principle in TypeScript”

  1. Worth mention the ICar and IPlane can have a common parent interface, IVechile w/o any method

Leave a comment

Create a website or blog at WordPress.com