How to use method overloading + method chaining in TypeScript?

75 Views Asked by At

I got an abstract superclass A which is extended by implementations B and C.

In this superclass, I'd like to use method overloading to change the return type depending on whether a parameter is given or not.

The key is that this method returns "this", so that I can use method chaining.

I got this:

abstract class A {
    abstract doSomething(a: string): Promise<A>;
    abstract doSomething(a: string, b: number): A;
}

class B extends A {
    async doSomething(a: string): Promise<A> {
        console.log(a);
        return this;
    }
}

class C extends A {
    doSomething(a: string, b: number): A {
        console.log(a, b);
        return this;
    }
}

Unfortunately the implementations of both C and B have an error that read:

TS2416: Property 'doSomething' in type 'C' is not assignable to the same property in base type 'A'.
   Type '(a: string, b: number) => A' is not assignable to type '{ (a: string): Promise<A>; (a: string, b: number): A; }'.

And both return statements have the error:

TS2322: Type 'this' is not assignable to type 'A'.
   Type 'C' is not assignable to type 'A'.
     Types of property 'doSomething' are incompatible.
       Type '(a: string, b: number) => A' is not assignable to type '{ (a: string): Promise<A>; (a: string, b: number): A; }'.

Is it even possible to do this?

Thanks,

Eduardo

1

There are 1 best solutions below

0
Edy Bourne On BEST ANSWER

Nevermind, I found a way:

abstract class A<T = undefined | number> {
    abstract doSomething(a: string, b: T): T extends undefined ? Promise<A> : A;
}

class B implements A<undefined> {
    async doSomething(a: string): Promise<A> {
        console.log(a);
        return this;
    }
}

class C extends A<number> {
    doSomething(a: string, b: number): A {
        console.log(a, b);
        return this;
    }
}

Seems a bit convoluted, but does the job. Please post another answer still if you know of a better way.

Hope it helps someone out there!