Reputation: 1932
I want to override a method and pass different argument types to it:
class Base {
public myMethod(myString: string): undefined {
return;
}
}
class Child extends Base {
public myMethod(myNumber: number): undefined {
return super.myMethod(String(myNumber));
}
}
Yet this yields a typescript error:
Property 'myMethod' in type 'Child' is not assignable to the same property in base type 'Base'. Type '(myNumber: number) => undefined' is not assignable to type '(myString: string) => undefined'. Types of parameters 'myNumber' and 'myString' are incompatible. Type 'string' is not assignable to type 'number'.
Is there a way to do this without creating a typescript error?
Upvotes: 27
Views: 31741
Reputation: 3176
You CAN do this easily, as long as you create a third BaseClass
and extend both of your classes from it. Just make sure the function with different parameters is defined ONLY in the extended classes (so they don't overlap). Here's a trivial example of it in action (my function that is different is called specialFunction):
class BaseClass { // FirstClass and SecondClass both extend this class
constructor() {
this.name = ""
}
name: string
}
class FirstClass extends BaseClass {
specialFunction(name: string) { // <- this only has one parameter
this.name = name
}
}
class SecondClass extends BaseClass {
constructor() {
super()
this.num = 0
}
specialFunction(name: string, num: number) { // <- this has two parameters
this.name = name
this.num = num
}
num: number
}
Upvotes: -1
Reputation: 778
It seems there is a way that works, even for return types, at least with TypeScript v 4.02 using an interim class.
But this is breaking Liskov substitution principle since it changes the return type and is not handling the case where the parameter is a string.
So it worths being mentioned just for the sake of knowledge, but in a code review I would not accept this hack, since a subclass should be able to replace the base class without breaking the functionality.
class Base {
public myMethod(myString: string): string {
return myString + myString;
}
}
// weaken
// inspired by comment here: https://github.com/microsoft/TypeScript/issues/3402#issuecomment-385975990
class Interim extends Base {
public myMethod(x: any): any { return super.myMethod(x); }
}
class Child extends Interim {
public myMethod(myNumber: number): number {
return myNumber * myNumber;
}
}
// we can have a helper/utility for this
function weaken(klass: { new(...args: any[]): any; }, key: string) {
return class extends klass {
[key](...args: any[]) { super[key](...args); }
}
}
class ChildOther extends weaken(Base, 'myMethod') {
public myMethod(myNumber: number): number {
return myNumber * myNumber;
}
}
console.log((new Child()) instanceof Base); // true
console.log((new ChildOther()) instanceof Base); // true
console.log(new Base().myMethod('str')); // strstr
console.log(new Child().myMethod(3)); // 9
console.log(new ChildOther().myMethod(3)); // 9
Upvotes: 4
Reputation: 249666
As mentioned by others this is not a good idea because you break Liskov substitution.
What you can easily do, is provide an override that takes both string
and number
. This allows your class to still be used wherever the base class is expected.
class Base {
public myMethod(myString: string): undefined {
return;
}
}
class Child extends Base {
public myMethod(myNumberOrString: number | string): undefined {
if (typeof myNumberOrString === 'number') {
return super.myMethod(String(myNumberOrString));
} else {
return super.myMethod(myNumberOrString);
}
}
}
Upvotes: 11
Reputation: 220994
There is not a way to do this*. In TypeScript, inheritance implies subtyping, so you can't inherit from a base class while not being a valid subtype of it.
*
Technically not true but I'm not going to mention them because they're gross hacks that you shouldn't do.
Upvotes: 15