Start learning free

LESSON 187 OF 348 · OBJECT-ORIENTED PROGRAMMING

Method Overriding in TypeScript

Lesson Read the full explanation

TL ; DR:

When a child class defines a method with the same name as the parent's, the child's version overrides the parent's. Different objects can give the same method name different behavior.


class Dog {
    bark() { console.log("Woof!"); }
}
class Puppy extends Dog {
    bark() { console.log("Yip!"); }
}


Full lesson:

You've seen that a child class inherits the parent's methods. Overriding is what happens when the child wants to change one of those inherited methods.

Why override?

Subclasses are usually special cases of the parent. A Puppy is a Dog, but it sounds different — "Yip!" instead of "Woof!". Instead of inventing a new method name, you redefine bark() on the Puppy. Callers don't need to know which subclass they're holding — they just call bark() and the right version runs.

How it works in each language

  • C# — the parent method must be marked virtual, the child marks its replacement override. The compiler enforces this pairing.
  • Java — every non-final method is virtual by default; the @Override annotation is optional but tells the compiler to verify the signature matches.
  • Python — defining a method with the same name in the child class replaces the parent's. No keyword required.
  • JavaScript — same as Python: define the method again on the child class and it wins.
  • C++ — the parent method must be virtual; override on the child is optional but compiler-checked.

Your task

The Dog class already defines bark. Inside the Puppy subclass, override bark so it prints "Yip! I am " followed by the puppy's name. The harness creates a Puppy named "Buddy" and calls bark() on it.

Expected output:

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

    TYPESCRIPT · OBJECT-ORIENTED PROGRAMMING

    Method Overriding in TypeScript, explained

    What overriding means

    A child class inherits its parent's methods, but sometimes an inherited method is nearly right and needs different behaviour. A puppy is a dog, yet it says Yip! where a grown dog says Woof!. Overriding means defining a method in the child with the same name as one in the parent. The child's version replaces the parent's for objects of the child class. The parent class is left untouched.

    Why override instead of inventing a new name

    You could give the puppy a method called yip. But then every piece of code that works with dogs would have to know which kind it holds, to decide between bark and yip. With an override, callers just call bark() and the right version runs for whatever object they have. The caller stays simple, and the differences live inside the classes.

    How to override in TypeScript

    You write the method again in the child, with the same name and a compatible signature. No special keyword is required on either class:

    class Dog {
        name: string;
        constructor(name: string) { this.name = name; }
        bark(): void {
            console.log("Woof! I am " + this.name);
        }
    }
    
    class Puppy extends Dog {
        bark(): void {
            console.log("Yip! I am " + this.name);
        }
    }
    

    When bark is called on a Puppy, JavaScript looks for the method on the puppy's own class first and finds the override. When it is called on a plain Dog, it finds the original. The field name is inherited, which is why the overriding method can still use this.name.

    Rules for the new method

    The compiler checks that the override fits the parent's version, because code written for a Dog must still work when it receives a Puppy. If the child's method has an incompatible parameter list or return type, the compiler reports that the property is not assignable to the same property in the base type. An override may be more flexible than the original, for example by taking optional parameters, but it can't demand more or return something unrelated.

    The optional override keyword

    TypeScript also has an override modifier. Putting it before the method states your intention, and the compiler then checks that the parent has a method of that name:

    class Puppy extends Dog {
        override bark(): void {
            console.log("Yip! I am " + this.name);
        }
    }
    

    If you misspell the name, say override barkk(), the compiler reports that the member is not declared in the base class. Without the modifier, a misspelling would silently create a new method and the parent's bark would keep running. The lesson works without it.

    Extending instead of replacing

    An override can build on the parent's version by calling it through super:

    class Puppy extends Dog {
        bark(): void {
            super.bark();
            console.log("Yip!");
        }
    }
    

    For a puppy named Q this prints Woof! I am Q and then Yip!. The lesson itself replaces the output completely, so it doesn't use super.

    The exercise

    The starter code has Dog with its bark, an empty Puppy class that extends it, and code that creates a puppy named "Buddy" and calls bark(). Run as it is, it prints Woof! I am Buddy, because the empty child inherits everything.

    1. Declare the override. Inside Puppy, write bark(): void { }, the same name and return type as the parent's.
    2. Fill in the body. Log "Yip! I am " followed by this.name.
    class Dog {
        name: string;
        constructor(name: string) { this.name = name; }
        bark(): void {
            console.log("Woof! I am " + this.name);
        }
    }
    
    class Puppy extends Dog {
        bark(): void {
            console.log("Yip! I am " + this.name);
        }
    }
    
    const buddy: Puppy = new Puppy("Buddy");
    buddy.bark();
    

    The output is:

    Yip! I am Buddy
    

    The choice follows the object, not the variable's declared type. If you store the puppy in a variable typed as Dog, with const asDog: Dog = buddy;, then asDog.bark() still prints Yip! I am Buddy.

    Common mistakes

    • A different name. Bark() or barks() does not override bark(). Names are case-sensitive, and the parent's method keeps running.
    • A different signature. Changing the parameters or the return type to something incompatible is a compile error.
    • Forgetting this.. The name comes from the field, so the body needs this.name.
    • Wrong text. The new message starts with Yip! I am , with the space before the name.
    • Expecting the parent to change. Overriding in Puppy leaves Dog as it was. A plain Dog still says Woof!.

    Key takeaways

    • Defining a method with the same name in a child class overrides the parent's version for the child's objects.
    • TypeScript needs no keyword, but override lets the compiler catch misspelt names.
    • The override must stay compatible with the parent's method signature.
    • Use super.method() to run the parent's version from inside the override.
    Keep going with TypeScriptAbout 340 step-by-step lessons, from your first line of code to coding interviews. Free to start.
    Start learning free