Start learning free

LESSON 186 OF 348 · OBJECT-ORIENTED PROGRAMMING

Inheritance in TypeScript: extends and super

Lesson Read the full explanation

TL ; DR:

A child class can inherit from a parent class — it gets all the parent's fields and methods for free, and can add its own. This lets you reuse code instead of copy-pasting it.


class Puppy extends Dog {
    whine() { console.log("whimper"); }
}


Full lesson:

Inheritance is one of the four classic OOP pillars (along with encapsulation, polymorphism, and abstraction). The idea is simple: most real-world things are kinds of other things — a Puppy is a Dog, a Car is a Vehicle, a Manager is an Employee. When you model that "is-a" relationship in code, the child class gets the parent's data and behavior automatically.

What gets inherited?

  • Fields — the Puppy has a name field even though we only declared it on Dog.
  • Methods — the Puppy can call bark() even though we only defined it on Dog.
  • The contract — anywhere code expects a Dog, you can pass a Puppy (we'll see this when we cover polymorphism).

What you still write

  • The constructor usually has to forward arguments up to the parent's constructor (because the parent's fields need initializing).
  • Any new methods or new fields specific to the child.

Your task

Below the Dog class, create a Puppy class that inherits from Dog and adds one new method, whine(), that prints "whimper". The harness creates a Puppy named "Buddy", calls its inherited bark(), and then its new whine().

Expected output:

Woof! I am Buddy
whimper
STEP 1 OF 2
    Ctrl + Enter to check

    TYPESCRIPT · OBJECT-ORIENTED PROGRAMMING

    Inheritance in TypeScript, explained

    The idea

    Many things are special cases of other things. A puppy is a dog, a car is a vehicle, a manager is an employee. Inheritance models this "is a" relationship. You write a general class once, then create a more specific class that starts with everything the general one has and adds to it. The general class is the parent (or base class) and the specific one is the child (or subclass). The child reuses the parent's code instead of copying it.

    The syntax

    A child class names its parent after the keyword extends:

    class Puppy extends Dog {
        whine() {
            console.log("whimper");
        }
    }
    

    A Puppy object now has everything a Dog has:

    • The fields. Puppy has a name, although only Dog declares it.
    • The methods. A puppy can bark(), although only Dog defines that method.
    • The parent's type. Wherever the code expects a Dog, you can pass a Puppy. The next lessons use this.

    On top of that, the child adds its own members. whine exists on Puppy only. The inheritance goes one way: a plain Dog has no whine.

    A class can extend only one parent. A parent can have many children, though, and a child can itself be a parent, so chains such as Animal, Dog, Puppy are possible.

    The constructor

    In the lesson, Puppy has no constructor, and this is allowed. When a subclass omits it, JavaScript supplies one that passes all its arguments to the parent's constructor. So new Puppy("Buddy") runs Dog's constructor with "Buddy", and the name field gets set.

    If the child needs a constructor of its own, for example to take an extra argument, it has to call the parent's constructor with super(...), and that call must come before any use of this:

    class Puppy extends Dog {
        age: number;
        constructor(name: string, age: number) {
            super(name);
            this.age = age;
        }
    }
    

    The compiler reports 'super' must be called before accessing 'this' in the constructor of a derived class if the order is wrong.

    The exercise

    The starter code defines Dog with a name field, a constructor and a bark method that prints Woof! I am followed by the name. At the bottom it creates a Puppy named "Buddy", calls bark(), then calls whine(). The Puppy class itself is missing.

    1. Declare the child. Between the Dog class and the code that uses Puppy, write class Puppy extends Dog. No constructor is needed.
    2. Add the new behaviour. Define whine in the body, and have it log "whimper". Don't redefine bark, because the puppy gets it from Dog.
    class Dog {
        name: string;
        constructor(name: string) { this.name = name; }
        bark() {
            console.log("Woof! I am " + this.name);
        }
    }
    
    class Puppy extends Dog {
        whine() {
            console.log("whimper");
        }
    }
    
    const buddy = new Puppy("Buddy");
    buddy.bark();
    buddy.whine();
    

    The output is:

    Woof! I am Buddy
    whimper
    

    Both lines appear even though Puppy defines only one of the two methods. The checks buddy instanceof Puppy and buddy instanceof Dog are both true, since a puppy is a dog.

    Common mistakes

    • Declaring the class before its parent. class Puppy extends Dog needs Dog to exist already. Write the parent first.
    • Copying the parent's members. Pasting bark and name into Puppy defeats the purpose. Whatever the child shares with the parent should stay in the parent.
    • Calling a child method on a parent object. A plain Dog has no whine. new Dog("Rex").whine() is a compile error, and if run anyway it throws TypeError: d.whine is not a function.
    • Using this before super. In a constructor of a child class, call super(...) first.
    • Forgetting super arguments. If the parent's constructor needs name, a child constructor must pass it along. Otherwise the field stays unset.
    • Using inheritance for a relationship that is not "is a". A Dog has a name, but it is not a kind of name. Inherit only when the child really is a more specific version of the parent.

    Key takeaways

    • class Child extends Parent gives the child all the parent's fields and methods.
    • The child adds its own members without changing the parent.
    • A child without a constructor uses the parent's. A child with a constructor must call super(...) before touching this.
    • A child object can be used wherever the parent type is expected.
    Keep going with TypeScriptAbout 340 step-by-step lessons, from your first line of code to coding interviews. Free to start.
    Start learning free