最新消息:雨落星辰是一个专注网站SEO优化、网站SEO诊断、搜索引擎研究、网络营销推广、网站策划运营及站长类的自媒体原创博客

javascript - Trying to override and extend method signature in child class in Typescript - Stack Overflow

programmeradmin2浏览0评论

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. I'm aware that prior to es6 I could have called BaseClass.prototype.doSomething.call(this, param1).

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. I'm aware that prior to es6 I could have called BaseClass.prototype.doSomething.call(this, param1).

Share Improve this question asked Apr 19, 2018 at 19:47 Michael SutherlandMichael Sutherland 5471 gold badge5 silver badges16 bronze badges 2
  • 1 It seems like this would work if you change MyClass's method to be called doSomethingElse. You could even still call super.doSomething(param1). – djs Commented Apr 19, 2018 at 20:42
  • that would also work. I think the other issues is that the original methods are returning a reference to "this" .In typescript the type of the returned this is ParentClass which is not the same shape as ChildClass. So some methods of the child class will return a ParentClass "this" while the overridden methods will return a ChildClass "this". Both will actually return the same object but typescript doesn't seem to like it because it seems them as different types. – Michael Sutherland Commented Apr 19, 2018 at 22:21
Add a comment  | 

2 Answers 2

Reset to default 13

The problem as pointed out by others is that if param2 is required it breaks polymorphism:

// 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 :

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>(""); by not really inheriting BaseClass but rather inherit a class that excludes the doSomething method:

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. An example where you might typically use this could be with a base class Filter and a derived class FilterWithDelay. The parent class implements a method called setFilter. In OOP you might have an array of Filters which contains instances of both Filter and FilterWithDelay. You might typically want to iterate through the array and call setFilter on each object. 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.

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. This way setFilter can implement the common interface.

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);
  }
}
发布评论

评论列表(0)

  1. 暂无评论