Home > database >  Shadow parent function/method in Typescript
Shadow parent function/method in Typescript

Time:08-06

Consider the follow code:

export class Foo extends Function {
    bar() {}
}

export class Foo2 extends Foo {
    bar(a) {}
}

This throws the error Property 'bar' in type 'Foo2' is not assignable to the same property in base type 'Foo'. Type '(a: any) => any' is not assignable to type '() => any'..

Is there any way to tell typescript to allow me to have a child class shadow it's parent's method? I understand typescript wants polymorphism to work - if I call new Foo2.bar(), it needs to have something to run. I just want it to run Foo.bar in this case, or require me to cast to Foo first, or something.

Here's my use-case. I'm trying to create some .d.ts files for plain javascript classes. They aren't built using ES classes, but just with plain functions like so:

function Foo() {}
Foo.prototype.bar = function() {}
Foo.prototype.baz = function() {}

function Foo2() {}
Foo2.prototype = new Foo();
Foo2.prototype.bar = function(a) {}

In plain javascript, this is just fine. An instance of Foo2 will have a method for baz, inherited from Foo, and a method for bar taking 1 argument, which replaces it's parent's. This is allowed in other languages like C# as well with shadowing. But I don't know how to represent this behavior in Typescript. The only workaround I can think of is just to not use inheritance, and manually redeclare all inherited methods that aren't overwritten, but of course this would be a real pain to maintain so I'd love it if there was a workaround.

Any ideas?

CodePudding user response:

Solution 1

One idea is to use the Rest Parameters in the base class so descendent classes can override it with any number of arguments. This way the type of each function parameter is not erased.

declare class Foo extends Function {
  bar(...args: any[]): void;
}

declare class Foo2 extends Foo {
  bar(a: string): void
}

Solution 2

The equivalent from JavaScript to Typescript would be the following. Just use Function as the base type to assign every type of function. The drawback is that the function's parameter type is erased.

export class Foo extends Function {
  bar: Function = () => {}
}

export class Foo2 extends Foo {
  bar = (a: string) => {}
}

CodePudding user response:

Is there any way to tell typescript to allow me to have a child class shadow it's parent's method? I understand typescript wants polymorphism to work - if I call new Foo2.bar(), it needs to have something to run. I just want it to run Foo.bar in this case, or require me to cast to Foo first, or something.

No, it's not really possible. Other languages like Java and C# will automatically call the right overloaded method based on the signature, but that's not how things work in Javascript and therefore Typescript.

It is actually possible to provide multiple signatures for a function. The below example uses the first 2 definitions to determine the overloads, but then the third one is the real implementation:

export class Foo {
    bar(a: number): number;
    bar(a: string): string;
    bar(a: number|number): string|number {

    }
}

But with these overloads you are required to figure how the function was called and narrow the type.

If you extend in Typescript, the subclass must be compatible with the parent class. The easiest way to do this in your case seems to be like this:

export class Foo2 extends Foo {
    bar(a?: number) {
      if (a===undefined) return super.bar();
      
    }
}
  • Related