Skip to content

The value of generic async function fn() doesn't satisfy ReturnType<typeof fn> #50826

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

Closed
cefn opened this issue Sep 18, 2022 · 3 comments
Closed

Comments

@cefn
Copy link

cefn commented Sep 18, 2022

Bug Report

I have a compiler error that strikes me as a bug. At least I don't have another explanation. I have a line below, in which the compiler is complaining the return type of fn somehow doesn't satisfy ReturnType<typeof fn> which seems pretty fundamental.

  // Type 'Promise<unknown>' is not assignable to type 'ReturnType<AsyncFn>'.
  const promise: ReturnType<typeof factory> = factory();

As part of a pipeline for retrying async operations I have a ResolutionEvent return type that combines the type of outcome, the async function called, and the value returned. The most natural Generic derivation is from the async function like this.

interface ResolutionEvent<AsyncFn extends () => Promise<unknown>> {
  eventType: "resolved";
  factory: AsyncFn;
  value: Awaited<ReturnType<AsyncFn>>;
}

However, when attempting to construct a ResolutionEvent, Typescript isn't able to 'suspend' inference of the Promised value type, allowing it to be settled by the actual typing of the AsyncFn generic. It seems to decide immediately that the return value of the function is Promise<unknown> rather than wiring up inference from AsyncFn.

The compiler errors are like this...

export async function resolve<AsyncFn extends () => Promise<unknown>>(
  factory: AsyncFn
): Promise<ResolutionEvent<AsyncFn>> {
  // compiler error for `promise` assignment in line below:
  // Type 'Promise<unknown>' is not assignable to type 'ReturnType<AsyncFn>'.
  const promise: ReturnType<typeof factory> = factory();
  // Type 'unknown' is not assignable to type 'Awaited<ReturnType<AsyncFn>>'
  const value: Awaited<typeof promise> = await promise;
  return {
    eventType: "resolved",
    factory,
    value,
  };
}

Background

The approach of having a generic AsyncFn has a practical use because the author of the function is expected to add additional metadata like below. This will enable them to reconcile or log the ResolutionEvent results as they come in, for example...

const fn = () => new Promise((resolve) => setTimeout(resolve, timeout));
fn.requestId = requestId++;
yield fn;
@cefn
Copy link
Author

cefn commented Sep 18, 2022

Closing as duplicate of #50251

@cefn cefn closed this as completed Sep 18, 2022
@cefn
Copy link
Author

cefn commented Sep 18, 2022

@MartinJohns following up on your comment in #50251 #50251 (comment) I closed this ticket as a duplicate of #50251 Does that sound right, or is this a distinct case?

You mentioned there were plenty of issues like this, but I wasn't able to find an open duplicate tracking the (legitimate?) feature request. I'd be keen to track improved ReturnType inference from generic functions if there is work on it. Or is it not a legitimate feature request at all for some reason? The above line raises a very surprising error.

I guess Working as Intended implies no future feature work, while Design Limitation might mean it's worth documenting and addressing in the long term? Have the arguments already been long dismissed for this being a Design Limitation and therefore worth trying to fix?

@MartinJohns
Copy link
Contributor

You mentioned there were plenty of issues like this, but I wasn't able to find an open duplicate tracking the (legitimate?) feature request.

The open issue is #50371.

I guess Working as Intended implies no future feature work, while Design Limitation might mean it's worth documenting and addressing in the long term?

I've seen them used somewhat interchangeably, but otherwise the descriptions of the tags are easy to understand. "Working as intended" says that it's working as intended and not a bug, it doesn't mean there won't be any changes in the future.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants