Skip to main content

Keep the Promise

The Liskov Substitution Principle says: anywhere the code expects an interface, every implementation of that interface must work correctly. An implementation that technically fits but behaves badly — throws, does nothing, lies — breaks every function that trusted the interface.

An interface is a promise. fly() promises flying. A class that implements fly() by crashing has signed a contract it cannot keep.


A Substitute That Breaks the Deal

The Bird interface promises fly(). Sparrows deliver. Penguins are birds too — so it feels natural to implement the interface... by throwing an error, because penguins cannot fly.

Now airShow, which works for every honest bird, crashes on a penguin. The bug is not in airShow — it is in the substitute that broke the promise.

The penguin that crashes the show

airShow trusts the interface and calls fly() on every bird. The sparrow performs; the penguin throws and stops the show right there (the try/catch only softens the landing to show the message).

Prompt
Write a TypeScript program showing a Liskov Substitution violation: a Bird interface with a fly method, a Sparrow that flies, and a Penguin whose fly throws an error because penguins cannot fly. An airShow function calls fly on every bird; use try/catch around the show to print the crash message when the penguin performs.
Output: TypeScript file and captured output

Separate the Promises

The fix: promise only what is true. Split the fat promise into Flyer and Swimmer. The sparrow implements Flyer, the penguin implements Swimmer — and no class signs a contract it cannot keep.

The best part is who catches the mistake now: passing a penguin to airShow is no longer a crash while the program runs — it is a type error, reported before the code ever starts.

Every type keeps its promise

airShow accepts Flyer, poolShow accepts Swimmer. Each animal appears only where its promise holds — substitution is safe by construction.

Prompt
Fix the Liskov violation in TypeScript by splitting the promises: a Flyer interface with fly and a Swimmer interface with swim. Sparrow implements Flyer, Penguin implements Swimmer. Write airShow for flyers and poolShow for swimmers and run both, so every class only appears where its promise holds.
Output: TypeScript file and captured output
Every type keeps its promise

Any Flyer can join

The payoff of honest interfaces: a new Eagle joins the air show with no risk — anything that implements Flyer truly flies, so airShow never needs to doubt its input.

Prompt
Extend the TypeScript Flyer interface example with a new Eagle class and run an air show with several flyers, showing that any honest Flyer implementation can substitute safely.
Output: TypeScript file and captured output