在TypeScript中声明抽象方法

在TypeScript中声明抽象方法,typescript,Typescript,我正试图找出如何在TypeScript中正确定义抽象方法: 使用原始继承示例: class Animal { constructor(public name) { } makeSound(input : string) : string; move(meters) { alert(this.name + " moved " + meters + "m."); } } class Snake extends Animal { constru

我正试图找出如何在TypeScript中正确定义抽象方法:

使用原始继承示例:

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,所以它是类型化的,可能会被高估


此外,我不确定如何正确定义受保护的
方法-它似乎是一个关键字,但没有任何效果,代码也不会编译。

名称
属性标记为受保护的
。这是在TypeScript 1.3中添加的,现在已牢固确立

makeSound
方法与类一样标记为
abstract
。现在不能直接实例化
动物
,因为它是抽象的,现在正式直播

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);
    }
}
不,不,不当语言不支持该功能时,请不要尝试创建自己的“抽象”类和方法;这同样适用于希望支持给定语言的任何语言功能。在TypeScript中没有实现抽象方法的正确方法。只需使用命名约定来构造代码,这样某些类就永远不会直接实例化,而不会显式地强制执行此禁止


此外,上面的示例只在运行时提供这种强制,而不是在编译时,正如您在Java/C中所期望的那样。

我相信,使用接口和基类的组合对您是可行的。它将在编译时强制执行行为需求(下面的rq_uuuPOST是指上面的一篇文章,而不是这篇)

接口设置基类不满足的行为API。您将无法设置基类方法来调用接口中定义的方法(因为如果不定义这些行为,您将无法在基类中实现该接口)。也许有人能想出一个安全的窍门,允许在父级中调用接口方法

您必须记住在将实例化的类中进行扩展和实现。它满足了关于定义运行时失败代码的担忧。如果没有实现接口(例如,如果尝试实例化Animal类),您甚至无法调用会呕吐的方法。我尝试让接口扩展下面的BaseAnimal,但它对Snake隐藏了BaseAnimal的构造函数和“name”字段。如果我能够做到这一点,那么使用模块和导出就可以防止BaseAnimal类的意外直接实例化

将此粘贴到此处以查看是否适合您:

我在下面的链接中看到了FristvanCampen的答案。他说抽象类是一种反模式,并建议使用实现类的注入实例实例化基本“抽象”类。这是公平的,但也有相反的论点。请自己阅读:

第2部分: 我还有一个例子,我想要一个抽象类,但是上面的解决方案阻止了我,因为“抽象类”中定义的方法需要引用匹配接口中定义的方法。所以,我采纳了弗里斯万坎本的建议。我有一个不完整的“抽象”类,带有方法实现。我与未实现的方法有接口;此接口扩展了“抽象”类。然后,我有一个类扩展了第一个类并实现了第二个类(它必须同时扩展这两个类,否则超级构造函数将无法访问)。请参见下面的(不可运行)示例:

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;
    }
}
导出类OntologyConceptFilter扩展FilterWidget.FilterWidget实现FilterWidget.IFilterWidget{
subMenuTitle=“呈现的本体”;//重载还是遮蔽?
建造师(
公共概念图:概念图。概念图,
graphView:PathToRoot.ConceptPathStorRoot,
实现:FilterWidget.IFilterWidget
){
超级(graphView);
this.implementation=this;
}
}

导出类过滤器Widget{
公共实现:IFilterWidget
filterContainer:JQuery;
public subnumtitle:string;//子项中的给定值
建造师(
公共图形视图:graphView.graphView
){
}
doStuff(节点:N){
this.implementation.generateStuff(东西);
}
}
导出接口IFilterWidget扩展FilterWidget{
generateStuff(节点:N):字符串;
}

如果您进一步了解Erics的答案,您实际上可以创建一个相当不错的抽象类实现,它完全支持多态性,并且能够从基类调用实现的方法。让我们从代码开始:

/**
 * 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
的实现,因此如果没有抽象方法的有效实现,就不可能构造
Animal
类型的对象。请注意,要使多态性发挥作用,您需要传递
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答案的主要区别在于“抽象”基类需要接口的实现,因此不能单独实例化

我用于在基类中引发异常

protected abstractMethod() {
    throw new Error("abstractMethod not implemented");
}
然后必须在子类中实现。 缺点是没有构建错误,而是运行时错误。优点是您可以从超类调用此方法,假设它可以工作:)


Milton

对我来说,至少对于Typescript v1,我不能从构造函数中引用“this”来传递给super。想法?您使用的编译器的确切版本是什么?您会遇到什么错误?tsc 1.0.1完美地编译了上述代码段。super()中不允许使用“this”关键字。我用的是TSC1.0.3,这很奇怪。您是否使用CLI
//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();
}
protected abstractMethod() {
    throw new Error("abstractMethod not implemented");
}