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

Nested Promise not automatically unwrapped #59111

Open
eps1lon opened this issue Jul 2, 2024 · 2 comments
Open

Nested Promise not automatically unwrapped #59111

eps1lon opened this issue Jul 2, 2024 · 2 comments
Assignees
Labels
Needs Investigation This issue needs a team member to investigate its status.

Comments

@eps1lon
Copy link
Contributor

eps1lon commented Jul 2, 2024

πŸ”Ž Search Terms

return type promise union

πŸ•— Version & Regression Information

This is the behavior in every version I tried, and I reviewed the FAQ for entries about Promise

⏯ Playground Link

https://www.typescriptlang.org/play/?#code/C4TwDgpgBAyg9gWwgOTgE2gXigZ2AJwEsA7AcygB8oAFfRQnCAHjyLID4BYAKB4EMcIYgGMoAMwCuI4ITjEo+CMQz4AwgAtCAGzQAKAJQAuGnQQNm8JKgzsoAbx5QnCiMAn55teowB0inHBaAG4QugBEwBB4Yfo8AL48PJLSsvKKyhD4AIJgYLrCmjrpxpYo6BD69oncCbzcAPT1sOpwEjpQoJAFEMIA1lAARhLAUJl0+DiGPI1OACrg0ADkXmaMLAQkpOyLUAxQxHAjAjiEpMR8A1rQwHAdC1CLrJuLProATADMACwArLHc6RUOTygMyGm0en0-xmzlhAD0APw8IA

πŸ’» Code

type SomeNode = string | Promise<string>

async function renderChild(): Promise<SomeNode> {
    return Promise.resolve("test")
}

function renderApp(children: SomeNode) {

}

// Should typecheck but errors:
//  Type 'Promise<string>' is not assignable to type 'string'.(2345)
renderApp(renderChild())
//        ^?

πŸ™ Actual behavior

Argument of type 'Promise<SomeNode>' is not assignable to parameter of type 'SomeNode'.
  Type 'Promise<SomeNode>' is not assignable to type 'Promise<string>'.
    Type 'SomeNode' is not assignable to type 'string'.
      Type 'Promise<string>' is not assignable to type 'string'.(2345)
input.tsx(13, 11): Did you forget to use 'await'?

πŸ™‚ Expected behavior

No typechecking error

Additional information about the issue

Nodes in React are typed as type Node = AwaitedNodes | Promise<AwaitedNodes>. However, the return type of async Components is Promise<Node>. But TypeScript will not consider this a Component because Promise<Node> is not assignable to Node even though it should be since at runtime Promise<Promise<T>> will never be observable and always collapse to Promise<T>.

Users can either fix this by using an unwieldy Promise<Awaited<ReactNode>>.

We can also fix this at the type level by allowing Node | Promise<Node> as the return type. Though I suspect this just pushes the issue one Promise-wrapping level away when TypeScript could collapse wrapped Promises automatically.

Original issue: vercel/next.js#67365

@DanielRosenwasser
Copy link
Member

We can also fix this at the type level by allowing Node | Promise<Node> as the return type.

I think this is probably what I'd recommend. I believe @rbuckton did explore something where Promise<T> could be compatible with T | Promise<T>; but I don't know if it would have handled the more recursive case you have up there.

@andrewbranch andrewbranch added the Needs Investigation This issue needs a team member to investigate its status. label Jul 5, 2024
@rbuckton
Copy link
Member

rbuckton commented Jul 9, 2024

You can't actually have a Promise<Promise<T>> in JS, but we have no mechanism to recursively unwrap a type parameter on instantiation. I looked into this a few years ago in #37615 (and #37540), but that solution depended on introducing magic inference rules for Promise-likes that couldn't be applied generally to other types.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Needs Investigation This issue needs a team member to investigate its status.
Projects
None yet
Development

No branches or pull requests

4 participants