Наследование
Босс — это монстр с чем-то дополнительным: он сражается в несколько фаз. Можно скопировать весь класс Monster и добавить поле, но тогда каждое изменение монстров придётся делать дважды. Вместо этого один класс может расширять (extends) другой:
class Monster {
name: string;
health: number;
constructor(name: string, health: number) {
this.name = name;
this.health = health;
}
describe(): string {
return `${this.name} (здоровье: ${this.health})`;
}
}
class Boss extends Monster {
phase: number;
constructor(name: string, health: number, phase: number) {
super(name, health);
this.phase = phase;
}
}
const dragon = new Boss("Дракон", 300, 1);
console.log(dragon.describe());
console.log(dragon);Программа выводит:
Дракон (здоровье: 300)
Boss { name: "Дракон", health: 300, phase: 1 }class Boss extends Monster значит: у Boss есть всё, что есть у Monster, — поля name и health и метод describe, — плюс то, что добавляет сам Boss, здесь поле phase. Это называется наследованием: Boss наследует от Monster. Monster — родительский класс, а Boss — производный класс (или подкласс). Это похоже на interface Boss extends Monster из урока «Типы объектов», только класс наследует не одну форму, но и код: dragon.describe() работает, хотя в Boss нет собственного метода describe.
Конструктор родителя: super. Поля родителя задаёт конструктор родителя, поэтому конструктор производного класса должен первым делом вызвать его. super(name, health) запускает конструктор Monster с этими аргументами. Только после этого объект готов, и конструктор может заняться собственными полями. Без super TypeScript сообщит об ошибке:
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("Появляется босс!");
}
}TypeScript сообщает:
main.ts(14,5): error TS2377: Constructors for derived classes must contain a 'super' call.Если использовать this до super(…), вы получите 'super' must be called before accessing 'this' in the constructor of a derived class. (TS17009). А super проверяет свои аргументы, как любой вызов: TS2554, если одного не хватает. Производному классу, которому не нужен собственный конструктор, его можно не писать; тогда он пользуется родительским: если в Boss есть только phase: number = 1;, пишите new Boss("Дракон", 300).
Босс — это монстр. Везде, где ожидается Monster, подойдёт Boss: в массиве Monster[] или как аргумент function attack(monster: Monster). Но через тип Monster можно пользоваться только тем, что есть у каждого монстра. Если const monsters: Monster[] = [goblin, dragon];, то monsters[1].phase — это Property 'phase' does not exist on type 'Monster'. (TS2339).
Задание
- 1
Напишите класс
Dragon, который расширяетMonsterи добавляет полеfirePower(число). У его конструктора три параметра,name,healthиfirePower: первые два он передаёт вsuper, аfirePowerзадаёт сам. - 2
Над массивом
monstersсоздайтеconst dragon = new Dragon("Красный дракон", 300, 40);и добавьтеdragonв массив. Программа выводит:Гоблин (здоровье: 30) Волк (здоровье: 25) Красный дракон (здоровье: 300) - 3
Добавьте в
DragonметодbreatheFire(): string. Он возвращает имя и силу огня дракона так:Красный дракон дышит огнём: урон 40. В конце программы выведитеdragon.breatheFire(). (Вызывайте его уdragon, а не уmonsters[2]: через типMonsterTypeScript не знает, что это дракон.) Программа выводит:Гоблин (здоровье: 30) Волк (здоровье: 25) Красный дракон (здоровье: 300) Красный дракон дышит огнём: урон 40