implements
In lesson 4, you used interfaces to describe the shape of an object. An interface can also contain methods. For a method, you write its name, its parameters and its return type, without a body:
interface Character {
name: string;
attack(): number;
}Every Character has a name and a method attack that returns a number, the damage. The game can then work with any character, whatever class it is: function fight(party: Character[]).
A class can promise to have this shape: class Wizard implements Character. TypeScript then checks the class right where it's declared, and if the promise isn't kept, you get an error:
interface Character {
name: string;
attack(): number;
}
class Wizard implements Character {
name: string;
mana: number = 30;
constructor(name: string) {
this.name = name;
}
}TypeScript reports:
main.ts(6,7): error TS2420: Class 'Wizard' incorrectly implements interface 'Character'.
Property 'attack' is missing in type 'Wizard' but required in type 'Character'.The first line says which class doesn't fit which interface; the indented line says why: attack is missing. Add the method to the class, and the error goes away:
attack(): number {
return this.mana / 2;
}implements doesn't give the class anything: you still write every field and method yourself. It's a check, and also a note for whoever reads the code: “This class is meant to be a Character.” If a method has the wrong type, TypeScript tells you as well. When attack returns a text, you get Property 'attack' in type 'Wizard' is not assignable to the same property in base type 'Character'. (TS2416).
Shapes, not names. TypeScript compares shapes, so any object with a name and an attack method is a Character, even without implements, even a plain object:
interface Character {
name: string;
attack(): number;
}
class Wizard implements Character {
name: string;
mana: number = 30;
constructor(name: string) {
this.name = name;
}
attack(): number {
return this.mana / 2;
}
}
class Archer {
name: string = "Kira";
attack(): number {
return 7;
}
}
const party: Character[] = [new Wizard("Lena"), new Archer(), { name: "Bruno", attack: () => 9 }];
for (const member of party) {
console.log(`${member.name}: ${member.attack()}`);
}This prints:
Lena: 15
Kira: 7
Bruno: 9Archer doesn't say implements Character, but it fits, so it can go into the array. With implements, though, you find out at the class itself that something is missing, not somewhere else later, where you first use it as a Character. And through the type Character, you can use only what the interface has: party[0].mana is Property 'mana' does not exist on type 'Character'. (TS2339), although the wizard does have mana.
Instructions
Write a class Mage that implements Character. It has the fields name and mana (a number), a constructor that sets both, and a method attack() that returns mana times 3.
Add new Mage("Lena", 5) to the party array. The program prints:
Bruno attacks for 12
Lena attacks for 15Every character should also be able to introduce itself. Add describe(): string; to the interface Character. TypeScript then reports both classes, so write the method in each of them: it returns the name followed by the warrior or the mage. Below the loop, add a second loop over party that prints member.describe(). The program prints:
Bruno attacks for 12
Lena attacks for 15
Bruno the warrior
Lena the mageStart programming for free
6/9