Start learning free

LESSON 641 OF 675 · OBJECT-ORIENTED PROGRAMMING

Polymorphism in TypeScript

Lesson

TL ; DR:

Polymorphism is when a single piece of code (like animal.speak()) does different things depending on which subclass the object actually is. You write the call once, and the right override runs automatically.


let animals = [new Dog(), new Cat()];
for (let a of animals) {
    a.speak();   // dispatches to Dog.speak then Cat.speak
}


Full lesson:

Polymorphism (Greek for "many forms") is what makes inheritance and interfaces actually useful at scale. Once Dog and Cat both inherit from Animal and override speak(), you can stop caring which one you're holding. Any code that takes an Animal and calls speak() works for both.

Why it matters

Imagine you're writing a function that prints what every animal in a zoo says:

function roll(animals: Animal[]): void {
    for (const a of animals) a.speak();
}

This function works for any current or future kind of animal. Add a Cow subclass tomorrow — this loop runs Cow.speak() automatically, no edits needed. That's the win: code that works today and keeps working as the type hierarchy grows.

Static type vs dynamic type

The reference is typed as Animal. The object it actually points to is a Dog, or a Cat, etc. Polymorphism is the language dispatching the method call based on the dynamic type (what it is at runtime) rather than the static type (what the variable was declared as).

Note for TypeScript

  • TypeScript — methods are looked up on the object at run time, so no virtual keyword is needed. Just override and it works.

Your task

Animal, Dog, and Cat are already defined, with the right overrides. Below the classes, declare a constant animals typed as Animal[] containing one Dog and one Cat, then loop through it with for...of, calling speak() on each.

Expected output:

Woof
Meow
STEP 1 OF 2
    Ctrl + Enter to check