Переопределение методов
Босс наследует describe от Monster, поэтому описывает себя как любой монстр: Дракон (здоровье: 300). Но босс должен ещё называть свою фазу. Производный класс может написать собственную версию метода родителя. Это называется переопределением метода:
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 = 1;
override describe(): string {
return `${super.describe()}, фаза ${this.phase}`;
}
}
const monsters: Monster[] = [new Monster("Гоблин", 30), new Boss("Дракон", 300)];
for (const monster of monsters) {
console.log(monster.describe());
}Программа выводит:
Гоблин (здоровье: 30)
Дракон (здоровье: 300), фаза 1Здесь три новых момента:
- Метод в
Bossназывается так же, как метод вMonster, поэтому для босса он заменяет унаследованный. Какая версия сработает, зависит от объекта, а не от типа переменной: в циклеmonsterдля TypeScript — этоMonster, но второй объект — босс, поэтому запускается его собственныйdescribe. super.describe()вызывает версию родителя. Боссу не нужно повторять, как монстры описывают себя: он берёт этот текст и добавляет фазу. (Осторожно:this.describe()вызвал бы собственный метод босса, а тот снова и снова вызывал бы сам себя, пока программа не остановится сRangeError: Maximum call stack size exceeded.)- Ключевое слово
overrideговорит: «Этот метод заменяет метод родителя». Оно необязательно, но позволяет TypeScript вас проверить.
Чем помогает override. Без него опечатка в имени ничего не заменяет, а просто добавляет новый метод:
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 = 1;
descibe(): string {
return `${this.name} (здоровье: ${this.health}), фаза ${this.phase}`;
}
}
const monsters: Monster[] = [new Monster("Гоблин", 30), new Boss("Дракон", 300)];
for (const monster of monsters) {
console.log(monster.describe());
}Программа выводит:
Гоблин (здоровье: 30)
Дракон (здоровье: 300)Метод босса не вызывается никогда, и ничто не подсказывает почему. Напишите override descibe(): string, и TypeScript найдёт ошибку:
main.ts(18,14): error TS4117: This member cannot have an 'override' modifier because it is not declared in the base class 'Monster'. Did you mean 'describe'?Переопределяющий метод должен ещё и подходить к родительскому: работать с теми же аргументами и возвращать значение того типа, который возвращает метод родителя. Если бы describe у босса возвращал число, TypeScript сообщил бы Property 'describe' in type 'Boss' is not assignable to the same property in base type 'Monster'. (TS2416). Поэтому босс может заменить монстра где угодно: всё, что можно делать с монстром, работает и с боссом.
Задание
- 1
Дракон должен описывать себя вместе с силой огня, но программа выводит лишь
Красный дракон (здоровье: 280). Напишитеoverrideперед методом дракона. Тогда TypeScript найдёт ошибку; исправьте её, чтобы программа вывела:Гоблин (здоровье: 10) Красный дракон (здоровье: 280), огонь 40 - 2
Метод дракона повторяет то, как описывают себя монстры. Используйте для этой части
super.describe(). Программа выводит то же самое. - 3
У драконов толстая чешуя: они получают только половину урона. Переопределите
takeDamageвDragon(сoverride) так, чтобы он вызывал версию родителя с половиной значения. Программа выводит:Гоблин (здоровье: 10) Красный дракон (здоровье: 290), огонь 40