Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Conditional method availability based on generic type state #56228

Closed
1 task done
vildanbina opened this issue Oct 27, 2023 · 2 comments
Closed
1 task done

Conditional method availability based on generic type state #56228

vildanbina opened this issue Oct 27, 2023 · 2 comments
Labels
Question An issue which isn't directly actionable in code

Comments

@vildanbina
Copy link

vildanbina commented Oct 27, 2023

Acknowledgement

  • I acknowledge that issues using this template may be closed without further explanation at the maintainer's discretion.

Comment

I'm working with TypeScript and have a base class, BaseClass, which uses a generic type IsManual to toggle its behavior. The class has a method manually() to update the _isManually state variable. Another method, callRequest(), changes its behavior based on this _isManually state.

Here's the relevant code:

type ManualValue = true | false;

export class Request {
    request() {
        return fetch('SOME_ROUTE');
    }

    foo() {
        return 1;
    }
}

export default class BaseClass<IsManual extends ManualValue = false> {
    _isManually: IsManual = false as IsManual;

    manually(): Omit<this, keyof BaseClass<any>> & BaseClass<true>;
    manually(value: true): Omit<this, keyof BaseClass<any>> & BaseClass<true>;
    manually(value: false): Omit<this, keyof BaseClass<any>> & BaseClass<false>;
    manually(value?: ManualValue): BaseClass<ManualValue> {
        if (value === undefined) {
            this._isManually = true as IsManual;
        } else {
            this._isManually = value as IsManual;
        }
        return this as BaseClass<IsManual>;
    } 
}

export class TestBaseClass extends BaseClass {
    callRequest(payload?: any) {
        const request = new Request();

        if (this._isManually) {
            return request;
        }

        return request.request();
    }
}

const t = new TestBaseClass();

t.manually().callRequest();

I expect that after calling t.manually().callRequest(), TypeScript would dynamically adjust method availability based on the value of _isManually. Specifically, I want to access methods from either the Request class or its request() method depending on the _isManually flag.

However, this is not happening as expected. How can I make TypeScript recognize the methods conditionally?

You can experiment with the code in this TypeScript Playground link

Further Attempts:

When I tried to chain the methods like this:

t.manually().callRequest().request();

I received a TypeScript error:

Property 'request' does not exist on type 'Request | Promise<Response>'.

This contradicts my expectations based on the _isManually flag, as I expected TypeScript to recognize that callRequest() would return an instance of the Request class and allow me to call its request() method.

I also attempted to use TypeScript's conditional types in the callRequest() method to directly depend on the _isManually property, like so:

export class TestBaseClass extends BaseClass {
    callRequest(payload?: any): this['_isManually'] extends true ? Request : Promise<Response> {
        const request = new Request();

        if (this._isManually) {
            return request as any;
        }

        return request.request() as any;
    }
}

I hoped that by doing this, the TypeScript compiler would understand the conditional return types and adjust the method suggestions accordingly.

Outcome:
Regardless of whether I called the manually() method or not, TypeScript did not adjust the available methods based on the _isManually flag. The issue remained unchanged: TypeScript did not recognize the methods I expected to be available.

@jcalz
Copy link
Contributor

jcalz commented Oct 27, 2023

The only way this sort of thing would work is with #6223 (or #1213). This isn't Stack Overflow so I don't think anyone here is going to be spending much effort resolving this for you.

@RyanCavanaugh RyanCavanaugh added the Question An issue which isn't directly actionable in code label Oct 27, 2023
@typescript-bot
Copy link
Collaborator

This issue has been marked as "Question" and has seen no recent activity. It has been automatically closed for house-keeping purposes.

@typescript-bot typescript-bot closed this as not planned Won't fix, can't repro, duplicate, stale Oct 30, 2023
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Question An issue which isn't directly actionable in code
Projects
None yet
Development

No branches or pull requests

4 participants