Repository navigation
Nested Promise not automatically unwrappedΒ #59111
Description
Activity
DanielRosenwasser commented
on Jul 2, 2024 MemberMore actionsWe 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 Ron Buckton (@rbuckton) did explore something where
Promise<T>could be compatible withT | Promise<T>; but I don't know if it would have handled the more recursive case you have up there.- addedNeeds InvestigationThis issue needs a team member to investigate its status.This issue needs a team member to investigate its status.
on Jul 5, 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 forPromise-likes that couldn't be applied generally to other types.eps1lon commented
on Dec 9, 2024 ContributorAuthorMore actionsWe 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.
Going with this approach for now (DefinitelyTyped/DefinitelyTyped#71379).
Sebastian "Sebbie" Silbermann (@eps1lon) i believe that with your change, you broke the types in this kind of code
const P: React.FC = () => "test"; function App() { return ( <div> {P({})} </div> ); }
this is the error on
Pinvocation:Type 'ReactNode | Promise<ReactNode>' is not assignable to type 'ReactNode'. Type 'Promise<ReactNode>' is not assignable to type 'ReactNode'. Type 'Promise<ReactNode>' is not assignable to type 'Promise<AwaitedReactNode>'. Type 'ReactNode' is not assignable to type 'AwaitedReactNode'. Type 'Promise<AwaitedReactNode>' is not assignable to type 'AwaitedReactNode'.(2322) const P: React.FunctionComponent (props: {}) => ReactNode | Promise<ReactNode>Reacted by Mykhaylo RyechkinKind of related (the other side of the problem) : this piece of code passes type check but does not work actually :
function join<T>(outer: Promise<Promise<T>>): Promise<T> { const promise = new Promise<T>((resolve) => { outer.then(inner => inner.then(value => resolve(value))) }) return promise }
Since the type
Promise<Promise<T>>cannot exist in javascript, it makesPromisea special case that would justify custom logic to forbid it IMHO. However, unlike the initial problem that arises in React, thejoinfunction is useless so this side of the problem is probably not worth considering.
π 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
π Actual behavior
π 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 isPromise<Node>. But TypeScript will not consider this a Component becausePromise<Node>is not assignable toNodeeven though it should be since at runtimePromise<Promise<T>>will never be observable and always collapse toPromise<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