简体   繁体   English

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

[英]Trying to override and extend method signature in child class in Typescript

I have a base class that I am trying to extend: 我有一个我要扩展的基类:

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

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

My class: 我的课:

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

but I'm getting a warning: 但我收到警告:

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>'.

Is it not possible to extend method signatures in typescript? 不能在打字稿中扩展方法签名吗? How do I extend the capabilities of the BaseClass if I need to add a parameter to the overridden method and is this the correct way of calling the parent method in es6 syntax. 如果需要向重写的方法添加参数,如何扩展BaseClass的功能,这是用es6语法调用父方法的正确方法。 I'm aware that prior to es6 I could have called BaseClass.prototype.doSomething.call(this, param1). 我知道在es6之前我可以调用BaseClass.prototype.doSomething.call(this,param1)。

The problem as pointed out by others is that if param2 is required it breaks polymorphism: 其他人指出的问题是,如果需要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

One solution, is to make the second parameter optional, so the call baseRef.doSomething("") is valid for the derived type as well : 一种解决方案是使第二个参数为可选,因此调用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;
    }
}

A second solution, if we only want to share code between the classes, is to disallow the assignment let baseRef: BaseClass<SomeOtherClass> = new MyClass<SomeOtherClass>(""); 如果我们只想在类之间共享代码,第二种解决方案是不允许分配let baseRef: BaseClass<SomeOtherClass> = new MyClass<SomeOtherClass>(""); by not really inheriting BaseClass but rather inherit a class that excludes the doSomething method: 通过不真正继承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>("")    ;

This is a violation of OOP as it would break polymorphism. 这违反了OOP,因为它将破坏多态性。 An example where you might typically use this could be with a base class Filter and a derived class FilterWithDelay. 一个典型的示例可能是基类Filter和派生类FilterWithDelay。 The parent class implements a method called setFilter. 父类实现一个称为setFilter的方法。 In OOP you might have an array of Filters which contains instances of both Filter and FilterWithDelay. 在OOP中,您可能有一个包含一组Filter和FilterWithDelay实例的Filters数组。 You might typically want to iterate through the array and call setFilter on each object. 您可能通常需要遍历数组并在每个对象上调用setFilter。 For this to be possible the child method should implement the same function signature as the parent method. 为此,子方法应实现与父方法相同的功能签名。 Otherwise the code, would need to check each instance to see if its a Filter or FilterWithDelay in order to pass in the additional parameters. 否则,代码将需要检查每个实例以查看其实例是Filter还是FilterWithDelay,以便传递其他参数。

In order to implement the FilterWithDelay you can extend the parent class and pass the delay as a parameter to the constructor instead of the method. 为了实现FilterWithDelay,您可以扩展父类并将延迟作为参数而不是方法传递给构造函数。 This way setFilter can implement the common interface. 这样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);
  }
}

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM