Search code examples
typescriptoverloadingabstract-classtypescript4.0

How to use method overloading + method chaining in TypeScript?


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


Solution

  • 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!