Declarando método abstrato em TypeScript
Estou tentando descobrir como definir corretamente métodos abstratos em TypeScript:
Usando o exemplo original de herança:
class Animal {
constructor(public name) { }
makeSound(input : string) : string;
move(meters) {
alert(this.name + " moved " + meters + "m.");
}
}
class Snake extends Animal {
constructor(name) { super(name); }
makeSound(input : string) : string {
return "sssss"+input;
}
move() {
alert("Slithering...");
super.move(5);
}
}
Eu gostaria de saber como definir corretamente o método makeSound, para que ele seja tipado e possa ser sobrescrito.
Além disso, não tenho certeza de como definir corretamente os métodos protected - parece ser uma palavra-chave, mas não tem efeito e o código não compilará.
Resposta #1
A propriedade name é marcada como protected. Isso foi adicionado no TypeScript 1.3 e agora está firmemente estabelecido.
O método makeSound está marcado como abstract, assim como a classe. Você não pode instanciar diretamente um Animal agora, porque ele é abstrato. Isto faz parte do TypeScript 1.6, que agora está oficialmente disponível.
abstract class Animal {
constructor(protected name: string) { }
abstract makeSound(input : string) : string;
move(meters) {
alert(this.name + " moved " + meters + "m.");
}
}
class Snake extends Animal {
constructor(name: string) { super(name); }
makeSound(input : string) : string {
return "sssss"+input;
}
move() {
alert("Slithering...");
super.move(5);
}
}
A maneira antiga de imitar um método abstrato era lançar um erro se alguém o utilizasse. Você não precisará mais fazer isso uma vez que o TypeScript 1.6 seja incluído em seu projeto:
class Animal {
constructor(public name) { }
makeSound(input : string) : string {
throw new Error('This method is abstract');
}
move(meters) {
alert(this.name + " moved " + meters + "m.");
}
}
class Snake extends Animal {
constructor(name) { super(name); }
makeSound(input : string) : string {
return "sssss"+input;
}
move() {
alert("Slithering...");
super.move(5);
}
}
Resposta #2
Se você levar a resposta do Eric um pouco mais adiante, pode na verdade criar uma implementação bastante decente de classes abstratas, com suporte completo a polimorfismo e a capacidade de chamar métodos implementados da classe base. Vamos começar com o código:
/**
* The interface defines all abstract methods and extends the concrete base class
*/
interface IAnimal extends Animal {
speak() : void;
}
/**
* The abstract base class only defines concrete methods & properties.
*/
class Animal {
private _impl : IAnimal;
public name : string;
/**
* Here comes the clever part: by letting the constructor take an
* implementation of IAnimal as argument Animal cannot be instantiated
* without a valid implementation of the abstract methods.
*/
constructor(impl : IAnimal, name : string) {
this.name = name;
this._impl = impl;
// The `impl` object can be used to delegate functionality to the
// implementation class.
console.log(this.name + " is born!");
this._impl.speak();
}
}
class Dog extends Animal implements IAnimal {
constructor(name : string) {
// The child class simply passes itself to Animal
super(this, name);
}
public speak() {
console.log("bark");
}
}
var dog = new Dog("Bob");
dog.speak(); //logs "bark"
console.log(dog instanceof Dog); //true
console.log(dog instanceof Animal); //true
console.log(dog.name); //"Bob"
Como a classe Animal exige uma implementação de IAnimal, é impossível construir um objeto do tipo Animal sem ter uma implementação válida dos métodos abstratos. Note que, para que o polimorfismo funcione, você precisa passar instâncias de IAnimal, não de Animal. Por exemplo:
//This works
function letTheIAnimalSpeak(animal: IAnimal) {
console.log(animal.name + " says:");
animal.speak();
}
//This doesn't ("The property 'speak' does not exist on value of type 'Animal')
function letTheAnimalSpeak(animal: Animal) {
console.log(animal.name + " says:");
animal.speak();
}
A principal diferença aqui com a resposta do Eric é que a classe base "abstract" requer uma implementação da interface e, portanto, não pode ser instanciada por conta própria.
