A_A
A_A

Reputation: 1932

Override method with different argument types in extended class - Typescript

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

Answers (4)

Jared
Jared

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

Catalin Enache
Catalin Enache

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

Titian Cernicova-Dragomir
Titian Cernicova-Dragomir

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

Ryan Cavanaugh
Ryan Cavanaugh

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

Related Questions