在TypeScript中声明抽象方法

我想弄清楚如何在TypeScript中正确定义抽象方法:

使用原始inheritance的例子:

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); } } 

我想知道如何正确定义方法makeSound,所以它是键入和可能过度。

此外,我不知道如何定义正确的protected方法 – 它似乎是一个关键字,但没有效果,代码将不会编译。

这个答案需要一个非常需要的更新,所以在这里!

name属性被标记为protected 。 这是在TypeScript 1.3中添加的,现在已经确立。

makeSound方法和类一样被标记为abstract 。 你现在不能直接实例化一个Animal ,因为它是抽象的。 这是TypeScript 1.6的一部分,现在正式正式运行。

 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); } } 

模仿抽象方法的旧方法是如果有人使用它,就会抛出一个错误。 一旦TypeScript 1.6登陆你的项目,你就不需要再做这个了:

 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); } } 

如果你把Erics回答得更远一些,你实际上可以创build一个相当不错的抽象类的实现,完全支持多态,并且可以从基类中调用实现的方法。 我们从代码开始:

 /** * 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" 

由于Animal类需要IAnimal的实现,因此不可能在没有有效实现抽象方法的情况下构造Animaltypes的对象。 请注意,为了使多态性正常工作,您需要传递IAnimal实例,而不是Animal 。 例如:

 //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(); } 

这里Erics的主要区别在于“抽象”基类需要实现接口,因此不能自行实例化。

我相信使用接口和基类的组合可以为你工作。 它将在编译时强制执行行为要求(rq_ post“below”是指上面的一个post,而不是这个)。

接口设置基类不符合的行为API。 您将无法设置基类方法来调用接口中定义的方法(因为无需定义这些行为就无法在基类中实现该接口)。 也许有人可以想出一个安全的技巧,允许在父级调用接口方法。

你必须记得在你要实例化的类中进行扩展和实现。 它满足了关于定义运行时失败代码的顾虑。 如果你还没有实现这个接口(例如,如果你尝试实例化动物类),你甚至不能调用会呕吐的方法。 我试着让界面扩展到下面的BaseAnimal,但它隐藏了Snake的构造函数和BaseAnimal的'name'字段。 如果我能够做到这一点,使用模块和导出可以防止意外的BaseAnimal类的直接实例化。

粘贴在这里看看它是否适合你: http : //www.typescriptlang.org/Playground/

 // The behavioral interface also needs to extend base for substitutability interface AbstractAnimal extends BaseAnimal { // encapsulates animal behaviors that must be implemented makeSound(input : string): string; } class BaseAnimal { constructor(public name) { } move(meters) { alert(this.name + " moved " + meters + "m."); } } // If concrete class doesn't extend both, it cannot use super methods. class Snake extends BaseAnimal implements AbstractAnimal { constructor(name) { super(name); } makeSound(input : string): string { var utterance = "sssss"+input; alert(utterance); return utterance; } move() { alert("Slithering..."); super.move(5); } } var longMover = new Snake("windy man"); longMover.makeSound("...am I nothing?"); longMover.move(); var fulture = new BaseAnimal("bob fossil"); // compile error on makeSound() because it is not defined. // fulture.makeSound("you know, like a...") fulture.move(1); 

我遇到了FristvanCampen的答案,如下所示。 他说抽象类是一种反模式,并build议使用一个实现类的注入实例来实例化基类的“抽象”类。 这是公平的,但也有反驳的论据。 请阅读: https : //typescript.codeplex.com/discussions/449920

第2部分:我有另外一个我想要抽象类的例子,但是我不能使用上面的解决scheme,因为“抽象类”中定义的方法需要引用匹配接口中定义的方法。 所以,我使用了FristvanCampen的build议。 我有不完整的“抽象”类,有方法实现。 我有与未实现方法的接口; 这个接口扩展了“抽象”类。 然后我有一个类扩展了第一个和实现第二个(它必须扩展,因为超级构造函数是无法访问否则)。 看下面的(不可运行的)示例:

 export class OntologyConceptFilter extends FilterWidget.FilterWidget<ConceptGraph.Node, ConceptGraph.Link> implements FilterWidget.IFilterWidget<ConceptGraph.Node, ConceptGraph.Link> { subMenuTitle = "Ontologies Rendered"; // overload or overshadow? constructor( public conceptGraph: ConceptGraph.ConceptGraph, graphView: PathToRoot.ConceptPathsToRoot, implementation: FilterWidget.IFilterWidget<ConceptGraph.Node, ConceptGraph.Link> ){ super(graphView); this.implementation = this; } } 

 export class FilterWidget<N extends GraphView.BaseNode, L extends GraphView.BaseLink<GraphView.BaseNode>> { public implementation: IFilterWidget<N, L> filterContainer: JQuery; public subMenuTitle : string; // Given value in children constructor( public graphView: GraphView.GraphView<N, L> ){ } doStuff(node: N){ this.implementation.generateStuff(thing); } } export interface IFilterWidget<N extends GraphView.BaseNode, L extends GraphView.BaseLink<GraphView.BaseNode>> extends FilterWidget<N, L> { generateStuff(node: N): string; } 

我用来在基类中抛出一个exception。

 protected abstractMethod() { throw new Error("abstractMethod not implemented"); } 

那么你必须在子类中实现。 缺点是没有构build错误,但运行时间。 优点是你可以从超类调用这个方法,假设它会工作:)

HTH!

米尔顿

不不不! 当语言不支持该function时,请不要尝试制作自己的“抽象”类和方法; 您希望支持某种语言的任何语言function也是如此。 在TypeScript中没有正确的方法来实现抽象方法。 只要使用命名约定来构build代码,以便某些类不会直接实例化,但不会明确强制执行这一禁止。

另外,上面的例子只是在运行时提供这个强制执行,而不是在编译时,就像在Java / C#中所期望的那样。