【问题标题】:Trying to override and extend method signature in child class in Typescript试图在 Typescript 的子类中覆盖和扩展方法签名
【发布时间】:2018-09-30 10:21:04
【问题描述】:

我有一个要扩展的基类:

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)。

【问题讨论】:

  • 如果您将MyClass 的方法更改为doSomethingElse,这似乎可行。你甚至可以打电话给super.doSomething(param1)
  • 这也可以。我认为其他问题是原始方法正在返回对“this”的引用。在打字稿中,返回的 this 的类型是 ParentClass,它与 ChildClass 的形状不同。因此,子类的某些方法将返回 ParentClass “this”,而被覆盖的方法将返回 ChildClass “this”。两者实际上都会返回相同的对象,但打字稿似乎不喜欢它,因为它们看起来是不同的类型。

标签: javascript typescript inheritance


【解决方案1】:

其他人指出的问题是,如果需要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;
    }
}

如果我们只想在类之间共享代码,第二种解决方案是通过不真正继承 BaseClass 而是继承排除 doSomething 方法的类来禁止分配 let baseRef: BaseClass&lt;SomeOtherClass&gt; = new MyClass&lt;SomeOtherClass&gt;("");

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>("")    ;

【讨论】:

    【解决方案2】:

    这违反了 OOP,因为它会破坏多态性。您可能通常使用它的示例可能是基类 Filter 和派生类 FilterWithDelay。父类实现了一个名为 setFilter 的方法。在 OOP 中,您可能有一个 Filter 数组,其中包含 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);
      }
    }
    

    【讨论】:

      猜你喜欢
      • 2017-02-16
      • 1970-01-01
      • 2011-03-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2018-07-08
      • 1970-01-01
      相关资源
      最近更新 更多