我有一个基本的遗产:
export abstract class Animal
{
constructor() {
}
makeSound() {
alert(this.sound);
}
}
export class Dog extends Animal
{
public sound: string = "Woof!";
constructor() {
super();
}
}
const dog = new Dog;
dog.makeSound(); // Woof!
我在试图让上述代码段工作时出现问题。 Property prop does not exist on type A
。但是,它确实存在于B上,并且由于它是公开的,因此A应该能够通过this.prop
到达道具。
我尝试了以下内容:
public prop: any;
添加到A
,则prop
会变为undefined
而非Hello world
。public abstract prop: any;
添加到A
,则会发生同样的事情。使用抽象类的整个继承点是共享this
范围。
如何使用Typescript正确完成?
答案 0 :(得分:7)
我同意JB Nizet。但您也可以将sound
定义为抽象属性。这样你的基类就知道sound
,所有扩展类都必须实现抽象属性sound
:
export abstract class Animal
{
abstract sound: string; // Only change here, now your code works
constructor() {
}
makeSound() {
alert(this.sound);
}
}
export class Dog extends Animal {
public sound: string = "Woof!";
constructor() {
super();
}
}
const dog = new Dog;
dog.makeSound(); // Woof!
定义抽象属性的能力来自TS 2.0:https://github.com/Microsoft/TypeScript/issues/4669
<强>更新强>
这种方法遇到的问题,参见注释,是抽象属性是在抽象基类的构造函数中使用的。这不起作用,因为在实例化对象之后,首先从派生类分配属性初始值。
这个问题有一个github issue,其解决方案是在抽象类的构造函数中访问抽象属性时出错。
答案 1 :(得分:1)
以下是您应该如何定义两个类:
javac
将属性放在Animal类中会告诉编译器所有动物都有声音。将声音作为超类构造函数的参数,可以清楚地表明所有子类在构造实例时都必须指定它们的声音。
还有其他可能的变体,但如果超类需要访问声音属性或某些getSound()方法,则此属性/方法应该存在于基类中。
答案 2 :(得分:1)
错误消息完全正确:prop
上不存在A
,因为它存在于B
上。你可能来自JavaScript,你做的就是好的。 TypeScript和JavaScript之间的主要区别在于TypeScript是强类型的,而JavaScript是弱类型的。这种差异需要改变你的想法。
在像JavaScript这样的弱类型语言中,prop
中不存在A
并不重要。类型在运行时解析,如果您的实际实例有prop
,一切都会好的。在静态(或强烈)类型的语言中,事物是不同的。类型在编译时强制执行,如果A
未定义prop
,则无法使用它。这就是强类型语言允许您定义抽象成员的原因:使它们可以在基类中访问,但实际上在派生类中定义rhem。
从not all versions of TypeScript support abstract properties directly开始,此解决方案在任何情况下都应该有效:
export abstract class Animal
{
get sound(): string { return getSound(); }
protected abstract getSound(): string;
constructor() {}
makeSound(): void {
console.log(this.sound);
}
}
export class Dog extends Animal
{
protected getSound: string { return "Woof!"; }
constructor() {
super();
}
}
const b = new B;
B.makeSound();