DEV Community

Rubén Alapont
Rubén Alapont

Posted on • Edited on

SOLID Principles Series: Demystifying the Liskov Substitution Principle (LSP) in Node.js with TypeScript

LSP: The Cornerstone of Classy Coding

The Liskov Substitution Principle, named after computer scientist Barbara Liskov, might sound like an arcane spell from the wizarding world, but it’s actually a fundamental concept in object-oriented programming. In simple terms, LSP states that objects of a superclass should be replaceable with objects of its subclasses without altering the correctness of the program.

Why LSP in Node.js with TypeScript?

Node.js, known for its JavaScript prowess, gets even more powerful with TypeScript. TypeScript adds a layer of types and interfaces, making it a perfect playground to implement and understand LSP.

Understanding LSP with a Node.js Example

Let’s dive into an example that illustrates LSP in action, using Node.js and TypeScript:

Step 1: Define a Superclass

First, we create a superclass that our subclasses will extend.

// vehicle.ts
export class Vehicle {
  startEngine(): string {
    return 'Engine started';
  }
}
Enter fullscreen mode Exit fullscreen mode

Step 2: Create a Subclass

Now, let's create a subclass that extends Vehicle.

// car.ts
import { Vehicle } from './vehicle';

export class Car extends Vehicle {
  startEngine(): string {
    return 'Car engine started';
  }
}
Enter fullscreen mode Exit fullscreen mode

Step 3: Another Subclass, With a Twist

Let’s add another subclass, but this time with a slight modification.

// electricCar.ts
import { Vehicle } from './vehicle';

export class ElectricCar extends Vehicle {
  startEngine(): string {
    return 'Electric Car engine started quietly';
  }

  chargeBattery(): string {
    return 'Battery charging';
  }
}
Enter fullscreen mode Exit fullscreen mode

The LSP Test

Now, let's test if ElectricCar and Car can be substituted for Vehicle without issues.

// app.ts
import { Vehicle } from './vehicle';
import { Car } from './car';
import { ElectricCar } from './electricCar';

function startVehicleEngine(vehicle: Vehicle) {
  console.log(vehicle.startEngine());
}

const myCar = new Car();
const myElectricCar = new ElectricCar();

startVehicleEngine(myCar); // 'Car engine started'
startVehicleEngine(myElectricCar); // 'Electric Car engine started quietly'
Enter fullscreen mode Exit fullscreen mode

The Catch: When LSP is at Risk

However, suppose we introduce a function that specifically uses ElectricCar's chargeBattery method:

function chargeElectricCarBattery(car: ElectricCar) {
  console.log(car.chargeBattery());
}
Enter fullscreen mode Exit fullscreen mode

If we try to pass a Car object to this function, we're violating LSP, as Car doesn't have the chargeBattery method. LSP reminds us to design our classes and hierarchies so that such situations are avoided or handled gracefully.

Conclusion: Embracing LSP in Your Node.js Endeavors

LSP encourages robust and maintainable object-oriented design. By adhering to LSP in Node.js with TypeScript, we ensure our code is flexible, intuitive, and less prone to errors.

And for those eager to continue unraveling the mysteries of coding principles, head over to ProductThinkers.com. It's a fantastic resource where you can explore, learn, and discuss all things related to product development and programming.

So, happy coding, and may your TypeScript adventures in Node.js be as smooth and error-free as an LSP-compliant superclass-subclass relationship! 🧩💻🚀

In the next article of our SOLID Principles Series, we'll dive into the "I" in SOLID: the Interface Segregation Principle (ISP). Stay tuned to continue your journey toward writing SOLID code in Node.js with TypeScript.

Top comments (3)

Collapse
 
loremimpsu profile image
Lorem Impsu

Wonderful post about SOLID, after learning about it, all software engineering careers were transformed.

Collapse
 
suichim profile image
Esteban Martini

I don't see the violation of the principle here, you can always replace any instance of Shape for any of Rectangle or Circle subclasses without problem.

Liskov substitution is more about respect the public api ( methods and params) than the implementation.

I mean in this case, both subclasses implements the area method without params, that able you that always can replace any Shape instance for any subclass and call the area method, without raising exceptions.

Another weird thing is that the base class don't have constructor, that is the place were the principle could be broke.

Collapse
 
michaelxie profile image
Michael-Xie

Don't really know what the difference are between them and why it is a violation of the L principle.

Different shapes should have different formulae to calculate their areas. The main difference I see is that one uses extends a class and the other uses implements of a typescript interface.

I would love a better explanation as I was more confused after reading this.