2017-01-10 85 views
1

我有一个基本的传承,像这样:Typescript不能识别父类的属性?

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达到道具。

我试过如下:

  • 同上 - 它不会编译。 (属性类型不存在于类型A上)
  • 如果我将public prop: any;添加到A,则prop将变为undefined而不是Hello world
  • 如果我将public abstract prop: any;添加到A,则会发生同样的情况。

通过使用抽象类的继承的全部点是分享this范围。

如何正确使用Typescript?

+0

不,这是自上而下的。一个苹果(B)是一个水果(A),但一个水果不是苹果。 继承添加或更改行为。你的B向A添加了一个属性。所以很明显A没有它。 – chris01

+0

@chris谨慎阐述? – Aris

+2

你假设A的任何实例也是B的一个实例。不是这样的:'C类扩展A {}'定义了一个扩展A的类,并且没有任何属性。 –

回答

4

我同意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,其中解决方案是简单地在抽象类的构造函数中访问抽象属性时出错。

+0

亚历克斯,当我尝试使用抽象关键字方法时,声音是'未定义的,所以它没有读取扩展类的值,正如我在我的问题中提到的。我会给这个试试看,很快就会通知你。编辑:是的,当我做'console.log(this.sound)'时,它输出'undefined'。我在打字机'2.1.4'上。 – Aris

+0

@IcarusCocksson在这里工作得很好:http://plnkr.co/edit/wRwff7MrFazrjYQHKmFI?p=preview –

+0

@JBNizet @Alex奇怪的是,它不适合我。如果你想看一下,我把提交到我的仓库中:https://github.com/Aristona/Phaser.Adventure-Capitalist/tree/master/src/Shop - 这是抽象类,第50-55行是在那里我定义了抽象属性。 Shops文件夹中的所有类都扩展Shop,并自定义这些属性。当我运行它时,它们都变成了“未定义”。 (Shop.ts中第50行和第55行之间的所有内容)。有任何想法吗? – Aris

1

这里是你如何定义你的两个类:

export abstract class Animal { 

    protected constructor(public sound: string) { 
    } 

    makeSound() { 
    alert(this.sound); 
    } 
} 

export class Dog extends Animal { 
    constructor() { 
    super('woof'); 
    } 
} 

把财产在动物类会告诉所有的动物都具有良好的编译器。将声音作为超类构造函数的一个参数使其清楚地表明,所有的子类在构造实例时都必须指定它们的声音。

还有其他可能的变化,但是如果超类需要访问sound属性或getSound()方法,那么这个属性/方法应该存在于基类中。

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(); 
+0

get/set语法是强制性的吗?另外,下面的语法看起来有点奇怪。 'protected getProp:string {return“Hello world”; }'。为什么我们将类型定义为字符串,但将对象文本与返回关键字放在一起? o_O – Aris

+0

@IcarusCocksson:'getProp'是一种方法,而不是属性。获取方法调用的语法是因为至少早期版本的TypeScript不支持抽象属性。因此使用getter并调用抽象方法。 – Sefe