Inheritance

A boss is a monster with something extra: it fights in phases. You could copy the whole Monster class and add a field, but then every change to monsters would have to be made twice. Instead, a class can extend another one:

class Monster {
    name: string;
    health: number;

    constructor(name: string, health: number) {
        this.name = name;
        this.health = health;
    }

    describe(): string {
        return `${this.name} (${this.health} health)`;
    }
}

class Boss extends Monster {
    phase: number;

    constructor(name: string, health: number, phase: number) {
        super(name, health);
        this.phase = phase;
    }
}

const dragon = new Boss("Dragon", 300, 1);
console.log(dragon.describe());
console.log(dragon);

This prints:

Dragon (300 health)
Boss { name: "Dragon", health: 300, phase: 1 }

class Boss extends Monster means: a Boss has everything a Monster has, the fields name and health and the method describe, plus whatever Boss adds, here the field phase. This is called inheritance: Boss inherits from Monster. Monster is the parent class, and Boss is a derived class (or subclass). It's like interface Boss extends Monster from lesson 4, except that a class inherits not only the shape, but also the code: dragon.describe() works, though Boss doesn't write a describe method itself.

The parent's constructor: super. The parent's fields are set by the parent's constructor, so the constructor of a derived class must call it first. super(name, health) runs the constructor of Monster with these arguments. Only then is the object ready, and the constructor can go on with its own fields. Without super, TypeScript reports:

class Monster {
    name: string;
    health: number;

    constructor(name: string, health: number) {
        this.name = name;
        this.health = health;
    }
}

class Boss extends Monster {
    phase: number = 1;

    constructor(name: string, health: number) {
        console.log("A boss appears!");
    }
}

TypeScript reports:

main.ts(14,5): error TS2377: Constructors for derived classes must contain a 'super' call.

If you use this before super(…), you get 'super' must be called before accessing 'this' in the constructor of a derived class. (TS17009). And super checks its arguments like any call: TS2554 when one is missing. A derived class that needs no constructor of its own can leave it out; it then uses the parent's: with just phase: number = 1; in Boss, you'd write new Boss("Dragon", 300).

A boss is a monster. Wherever a Monster is expected, a Boss fits: in a Monster[] array, or as the argument of function attack(monster: Monster). Through the type Monster, though, you can only use what every monster has. With const monsters: Monster[] = [goblin, dragon];, monsters[1].phase is Property 'phase' does not exist on type 'Monster'. (TS2339).

Instructions

Write a class Dragon that extends Monster and adds a field firePower (a number). Its constructor has three parameters, name, health and firePower: it passes the first two to super and sets firePower itself.

Above the monsters array, create const dragon = new Dragon("Red Dragon", 300, 40); and add dragon to the array. The program prints:

Goblin (30 health)
Wolf (25 health)
Red Dragon (300 health)

Add a method breatheFire(): string to Dragon. It returns the dragon's name and fire power like this: Red Dragon breathes fire: 40 damage. At the end of the program, print dragon.breatheFire(). (Call it on dragon, not on monsters[2]: through the type Monster, TypeScript doesn't know it's a dragon.) The program prints:

Goblin (30 health)
Wolf (25 health)
Red Dragon (300 health)
Red Dragon breathes fire: 40 damage

Start programming for free

By signing up, you agree to the Terms of Service and Privacy Policy.

Or sign up with:

7/9