Dieterg
Dieterg

Reputation: 16368

Typescript fluent api builder multiple parameters (override) not possible

Consider the following class which is declared in a 3rd party library. I cannot modify this file.

declare class Foo<T> {
    methodA(foo: string): this 
    methodB(): this;
    methodD(): this;
    methodD(type: string): this;
}

The above class is a fluent api which can calls itself. The requirement is that methodB should never be callable. Therefore I created the following type which will be used over Foo<T>

type FooWithoutB<T> = Omit<Foo<T>, 'methodB'>

But this still allows me to do the following:

let typeWithoutB!: WithoutB<any>;

typeWithoutB.methodA('hello').methodB() // should not be possible

So I thought, ok, let's override the return type, thus I made this type:

type Override<T> = {
    [key in keyof T]: T[key] extends (...param: any[]) => T ? (...params: Parameters<T[key]>) => WithoutB<T> : T[key]
}

The above almost works but breaks Parameter types:

foo.methodD() // expected one argument but got 0
foo.methodD('ok') // this works, but above should also work

See playground

Edit

Found out that type F = Parameters<Foo<any>['methodD']> returns [type: string]. Is there an option to resolve both parameter types?

Upvotes: 0

Views: 132

Answers (1)

Rich N
Rich N

Reputation: 9475

I don't think there's an easy answer here. One possibility is to fall back on classic OOP and introduce a facade. So we'd wrap an instance of the existing class in a new class, and forward calls where appropriate. Something like the code below. Note that this assumes that our MethodA and MethodDs return 'this'.

class FooFacade<T>
{
    private _foo: Foo<T> = new Foo<T>();
    methodA(foo: string): this { this._foo.methodA(foo); return this; }
    
    methodD(): this;
    methodD(type: string): this;
    methodD(type?: string): this {
        if (type !== undefined) {
            this._foo.methodD(type);
        } else {
            this._foo.methodD();
        }
        return this;
    }
}

The obvious drawback of this is it's a bit clunky and depending on how complex Foo actually is it could be a lot of work. An advantage is it gives you complete control over how the third-party software is called.

Upvotes: 2

Related Questions