尝试在Typescript中覆盖和扩展子类中的方法签名

时间:2018-04-19 19:47:26

标签: javascript typescript inheritance

我有一个我想扩展的基类:

export class BaseClass<T extends SomeOtherClass> {
  constructor(param: ParamType) {
  }

  doSomething(param1: Param1Type): BaseClass<T> {
    // do something with param1;
    return this;
  } 
}

我的课程:

export class MyClass<T extends SomeOtherClass> extends BaseClass<T> {

  constructor(param: ParamType) {
    super(param);
  }

  doSomething(param1: Param1Type, param2: Param2Type): MyClass<T> {
    // super.doSomething(param1);
    // do something with param2;
    return this;
  }
}

但是我收到了警告:

Property 'doSomething' in type 'MyClass<T>' is not assignable to the same property in base type 'BaseClass<T>'.
  Type '(param1: Param1Type, param2: Param2Type) => MyClass<T>' is not assignable to type '(param1: Param1Type) => BaseClass<T>'.

是否无法在打字稿中扩展方法签名?如果我需要向重写方法添加参数,如何扩展BaseClass的功能,这是在es6语法中调用父方法的正确方法。我知道在es6之前我可以调用BaseClass.prototype.doSomething.call(this,param1)。

2 个答案:

答案 0 :(得分:2)

其他人指出的问题是,如果需要param2,它会破坏多态性:

// We should be able to do this assignment 
let baseRef: BaseClass<SomeOtherClass> = new MyClass<SomeOtherClass>(""); 
baseRef.doSomething("") // param2 is not required by the base class so MyClass will not receive it even though it NEEDS it

一种解决方案是使第二个参数成为可选项,因此调用baseRef.doSomething("")也适用于派生类型:

export class MyClass<T extends SomeOtherClass> extends BaseClass<T> {

    constructor(param: string) {
        super(param);
    }

    doSomething(param1: string, param2?: string): MyClass<T> {
        super.doSomething(param1);
        return this;
    }
}

第二个解决方案,如果我们只想在类之间共享代码,那就是不通过继承let baseRef: BaseClass<SomeOtherClass> = new MyClass<SomeOtherClass>("");来禁止赋值BaseClass,而是继承一个排除doSomething的类方法:

type PartialBaseClass = new <T> (param: string)  => { [P in Exclude<keyof BaseClass<T>, 'doSomething'>] : BaseClass<T>[P] }
const PartialBaseClass:PartialBaseClass = BaseClass

export class MyClass<T extends SomeOtherClass> extends PartialBaseClass<T> {

    constructor(param: string) {
        super(param);
    }

    doSomething(param1: string, param2: string): MyClass<T> {
        BaseClass.prototype.doSomething.call(this, param1);
        return this;
    }
}
// This is now invalid ! 
let baseRef: BaseClass<SomeOtherClass> = new MyClass<SomeOtherClass>("")    ;

答案 1 :(得分:0)

这违反了OOP,因为它会破坏多态性。您通常可以使用它的示例可以是基类Filter和派生类FilterWithDelay。父类实现了一个名为setFilter的方法。在OOP中,您可能有一个Filters数组,其中包含Filter和FilterWithDelay的实例。您通常可能希望迭代数组并在每个对象上调用setFilter。为此,子方法应该实现与父方法相同的函数签名。否则代码将需要检查每个实例以查看其是否为Filter或FilterWithDelay以传递其他参数。

为了实现FilterWithDelay,您可以扩展父类并将延迟作为参数传递给构造函数而不是方法。这样setFilter就可以实现通用接口。

export class BaseClass<T extends SomeOtherClass> {
  constructor(param: ParamType) {
  }

  doSomething(param1: Param1Type): BaseClass<T> {
    // do something with param1;
    return this;
  } 
}

export class MyClass<T extends SomeOtherClass> extends BaseClass<T> {

  constructor(param: ParamType, param2: Param2Type) {
    super(param);
    this.param2 = param2;
  }

  doSomething(param1: Param1Type): MyClass<T> {
    return super.doSomething(param1 + this.param2);
  }
}