Last updated: Apr 29, 2026
Liskov Substitution
Subclasses must work wherever the base class works. If your code uses a parent class, it should be able to use any subclass without knowing which one it is. A subclass can add new behavior, but it cannot remove or break behavior the parent promised.
Two red flags that you are violating LSP:
- A subclass throws an exception for a method the parent provides
- Callers need
instanceofchecks to handle specific subclasses
Example
Penguin extends Bird but breaks the expectation that all birds can fly:
class Bird {
fly(): void {
// flying logic
}
}
class Penguin extends Bird {
fly(): void {
throw new Error("Penguins can't fly");
}
}
Split the flying behavior into its own interface so only birds that can actually fly implement it:
abstract class Bird {
abstract eat(): void;
}
abstract class FlyingBird extends Bird {
abstract fly(): void;
}
class Sparrow extends FlyingBird {
eat(): void {}
fly(): void {}
}
class Penguin extends Bird {
eat(): void {}
}
Now any code that works with Bird can accept a Penguin without breaking. Code that specifically needs a flying bird uses FlyingBird.