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

Can't use type aliases or conditional types resolving to Promise for async/await return types #27987

Open
tao-cumplido opened this issue Oct 19, 2018 · 11 comments
Labels
Awaiting More Feedback This means we'd like to hear from more people who would be helped by this feature Suggestion An idea for TypeScript

Comments

@tao-cumplido
Copy link
Contributor

TypeScript Version: 3.1

Search Terms:

  • async promise alias
  • async promise conditional

Code

type P<T> = Promise<T>

async function foo<T>(x: T): P<T> {
    return x;
}

async function bar<T>(x: T): T extends number ? Promise<number> : Promise<any> {
    return x;
}

Expected behavior:
No error.

Actual behavior:
Type 'P' is not a valid async function return type in ES5/ES3 because it does not refer to a Promise-compatible constructor value.

Type 'T extends number ? Promise : Promise' is not a valid async function return type in ES5/ES3 because it does not refer to a Promise-compatible constructor value.

Playground Link:
Link

Related Issues:

@weswigham
Copy link
Member

As a workaround, can you write Promise<T extends number ? number : any> instead?

@weswigham weswigham added Bug A bug in TypeScript Domain: Conditional Types The issue relates to conditional types labels Oct 19, 2018
@tao-cumplido
Copy link
Contributor Author

Yes, that works for my use case. Should have thought of it myself, thanks!

@swar30
Copy link

swar30 commented Oct 21, 2018

just had same issue, this looks to be regression in latest TS version (3.1.3) in prev versions this results in compilation error (as far as I can see).

Work around indeed works, but it would be better if this will break compilation, so will know immediately this is not supported

@trusktr
Copy link
Contributor

trusktr commented Nov 1, 2018

I just had this problem, and I did not think to place the whole conditional Promise<inside of here>.

Here's my playground example.

Thanks for the work around, I'll use that. A fix for this would be nice, so it works for people trying it for the first time (f.e. me).

@rbuckton
Copy link
Member

This is behaving by design. ES5 and earlier runtimes did not have a native Promise implementation, and ES5 down-level transpiled async functions use a "bring-your-own promise" solution that uses the return type position to determine which Promise implementation to use.

@swar30: Are you suggesting that T extends number ? Promise<number> : Promise<any> should also be an error in ES2015 and later?

@swar30
Copy link

swar30 commented Jan 17, 2019

@rbuckton I would say that compile time and run time should be aligned.
If one fails the other should fail as well in this specific case.

Actually I would have preferred it to work in both cases, meaning been able to use above type expressions and have the generated code actually work.

From what I saw, what happens is that while I would expect the types to be erased in compilation, in this specific case they are not, and there is still reference in transpiled code to P, which fails in runtime as webpack can't find such module.

@RyanCavanaugh RyanCavanaugh added Suggestion An idea for TypeScript Awaiting More Feedback This means we'd like to hear from more people who would be helped by this feature and removed Bug A bug in TypeScript Domain: Conditional Types The issue relates to conditional types labels Mar 13, 2019
@RyanCavanaugh RyanCavanaugh removed this from the TypeScript 3.4.0 milestone Mar 13, 2019
@mhchen
Copy link

mhchen commented May 24, 2020

It looks like this is still a problem, and furthermore that the workaround suggested by @.weswigham doesn't work as of Typescript 3.9.2:

async function test<T>(x: T): Promise<T extends any ? string : number> {
    return 'test';
}

Expected behavior:
No error

Actual behavior:

Type '"test"' is not assignable to type 'T extends any ? string : number'.

Even when I change the return type to T extends any ? any : any, I still get Type '"test"' is not assignable to type 'T extends any ? any : any'.

Let me know if:

  1. I did something wrong
  2. I should report this as a separate issue

@weswigham
Copy link
Member

Nah - it's a flaw in how we relation conditional types, which'd need #27932 or #30639 to fix.

@akatechis
Copy link

akatechis commented Jun 27, 2022

Is there a workaround for this in 2022? In my example, I have a module that makes API requests, and I'd love to refactor the return type as a Promise that resolves to either an Error or the Response type.

export type GRPCResult<T> = Promise<FP.Either<Error, T>>;
async function postFoo(call: CF.API.PostFooCall): GRPCResult<PostFooResponse> {
  // ...
}

@akatechis
Copy link

Looks like the workaround is to create a variable that aliases the Promise constructor at runtime in order to appease the TS compiler. My previous example now compiles with this:

const GRPCResult = Promise; // <---- workaround
// eslint-disable-next-line @typescript-eslint/no-redeclare
export type GRPCResult<T> = Promise<FP.Either<Error, T>>;

async function postFoo(call: CF.API.PostFooCall): GRPCResult<PostFooResponse> {
  // ...
}

It's annoying and concerning that this triggers a lint rule for redeclaring a variable, but I'm willing to live with this for now until this issue is fixed properly...

@tao-cumplido
Copy link
Contributor Author

@akatechis it should work without workaround when you set your target to ES2015 or later

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Awaiting More Feedback This means we'd like to hear from more people who would be helped by this feature Suggestion An idea for TypeScript
Projects
None yet
Development

No branches or pull requests

9 participants