You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
constp1=newPromise<Promise<Number>>((resolveOuter)=>{constinnerPromise=newPromise<Number>((resolveInner)=>{console.log('Resolving inner promise')resolveInner(1)})console.log('Resolving outer promise')resolveOuter(innerPromise)})p1.then((p2: Promise<Number>)=>{p2.then((num)=>console.log('the number is: ',num))})
Expected behavior:
Compilation should fail, because p1 is actually a Promise<number> due to promise unwrapping. Actual behavior:
Compilation should fail, requiring code which looks like:
const p1 = new Promise<Number>((resolveOuter) => {
const innerPromise = new Promise<Number>((resolveInner) => {
console.log('Resolving inner promise')
resolveInner(1)
})
console.log('Resolving outer promise')
resolveOuter(innerPromise)
})
p1.then((p2: Number) => {
console.log('the number is: ', p2)
})
Seems like a legit bug, but as a side note, a little strange to resolve a promise from a promise executor callback. Promise executor should basically be reduced down to the lowest level that isn't already promisified. If you already have a promise in the executor, then you could just make that your promise call, instead of creating a wrapper promise around a promise.
We have the same problem. We wrap unknown return types into an additional Promise and can get stuck with a resolved type of Promise<Promise<T>> if the original type was a Promise.
This is just how Promises work. You simply cannot get the "inner" Promise, it's gone, flattened.
I'm not saying that the type Promise<Promise<T>> is wrong, it might just be the result of a function, wrapping something into a Promise. However, TypeScript should always collapse nested Promises when type checking and compiling because that is what JavaScript does.
Reacted by Alexander Mills, Anton Ivanov, Philippe Lavoie, Huan Li, Joshua Baker, Jimmy Breck-McKye, Alex Yang, Ilias Gazdaliev, Glenn 'devalias' Grant, Daniel Rodríguez Rivero and 6 more
Reacted by Anton Ivanov, Joshua Baker, Ilias Gazdaliev, Tony Gentilcore, Glenn 'devalias' Grant and Victor Moraes
Reacted by Anton Ivanov, Joshua Baker, Ilias Gazdaliev, Tony Gentilcore and Glenn 'devalias' Grant
We are pushing awaited until after TS3.9 while we continue to assess the impact of the change. As a result, I am reopening this issue until we have a solution in place.
The solution would be for Promise not to re-wrap if it's already a Promise. That way, we can never end up with nested Promises. Here's a little proof of concept:
exporttypePromise<Aextendsany>=globalThis.Promise<AextendsglobalThis.Promise<infer X>
? X
: A>
Reacted by Joshua Baker, Matt, Alex Yang, Kisaragi, Glenn 'devalias' Grant and Victor Moraes
So what is the status of this? It is a real deal-braker for function composition. You can't do any composition of functions that returns promises because you end with Promise<Promise<Promise<T>>> which doesn't make any sense in JS
Sorry to necrobump, but is this really resolved? (no pun intended!)
On the latest tsc using myNestedPromise.then() still expects the callback to accept a promise rather than the awaited type, generating errors like no.ts:27:3 - error TS2739: Type 'Promise<Number>' is missing the following properties from type 'Number': toFixed, toExponential, toPrecision
Activity
DanielRosenwasser commentedon Oct 11, 2018
Ron Buckton (@rbuckton) do we have an issue tracking this?
All I have is #17077 and the design notes at #17621, #18155, #19169, #20724.
DanielRosenwasser commentedon Oct 25, 2018
Seems like you want something like negated types (#18280 or #4196).
the1mills commentedon Nov 1, 2018
Seems like a legit bug, but as a side note, a little strange to resolve a promise from a promise executor callback. Promise executor should basically be reduced down to the lowest level that isn't already promisified. If you already have a promise in the executor, then you could just make that your promise call, instead of creating a wrapper promise around a promise.
hcomnetworkers commentedon Mar 22, 2019
We have the same problem. We wrap unknown return types into an additional Promise and can get stuck with a resolved type of
Promise<Promise<T>>if the original type was a Promise.This nested Promise however is problematic:
Here,
yis wrongfully seen as aPromise<string>whilezis correctly seen asstring.Why does
awaitandthenbehave differently?Also why can't we assign a
Promise<Promise<T>>to aPromise<T>variable? This is valid JS since nested Promises always unwrap.ORESoftware commentedon Mar 23, 2019
hcomnetworkers
Promise<Promise<T>>can exist, it looks like thisit doesn't get unwrapped in this case since the executor resolves synchronously
hcomnetworkers commentedon Mar 26, 2019
Alexander Mills (@ORESoftware) It does not matter how often you nest a Promise, the result is the same:
This is just how Promises work. You simply cannot get the "inner" Promise, it's gone, flattened.
I'm not saying that the type
Promise<Promise<T>>is wrong, it might just be the result of a function, wrapping something into a Promise. However, TypeScript should always collapse nested Promises when type checking and compiling because that is what JavaScript does.Kinrany commentedon Mar 26, 2019
The
Promisetype seems broken:playground
ORESoftware commentedon Mar 27, 2019
hcomnetworkers you're right, never would have guessed that wrt to the promise executor
4 remaining items
rbuckton commentedon Mar 25, 2020
We are pushing
awaiteduntil after TS3.9 while we continue to assess the impact of the change. As a result, I am reopening this issue until we have a solution in place.millsp commentedon May 21, 2020
The solution would be for
Promisenot to re-wrap if it's already aPromise. That way, we can never end up with nestedPromises. Here's a little proof of concept:millsp commentedon May 21, 2020
So now we can do:
yume-chan commentedon Aug 21, 2020
awaitedkeyword was in the 4.0 iteration plan (#38510), but 4.0 just released without it.I can't find any more discussion about it. It's not in the 4.1 iteration plan (#40124), nor any Design Notes.
I found it being mentioned in #40002, but that pr didn't actually add the
Awaitedtype into libs right?What's the current state of this issue?
DanielRosenwasser commentedon Aug 21, 2020
#40006 mentions it, and #40002 mentions it. Anders Hejlsberg (@ahejlsberg) Ryan Cavanaugh (@RyanCavanaugh) we should determine whether the
Awaitedtype alias makes sense for 4.1.chore: await axios.request() (err handling fix)
chore: await axios.request() (err handling fix)
danielo515 commentedon Mar 4, 2021
So what is the status of this? It is a real deal-braker for function composition. You can't do any composition of functions that returns promises because you end with
Promise<Promise<Promise<T>>>which doesn't make any sense in JStimoxley commentedon May 4, 2021
Is there any generic workaround for this, even an ugly one?
emilyyyylime commentedon Mar 17, 2025
Sorry to necrobump, but is this really resolved? (no pun intended!)
On the latest
tscusingmyNestedPromise.then()still expects the callback to accept a promise rather than the awaited type, generating errors likeno.ts:27:3 - error TS2739: Type 'Promise<Number>' is missing the following properties from type 'Number': toFixed, toExponential, toPrecisionemilyyyylime commentedon Mar 17, 2025
or maybe this is just a library issue needing the signature of
Promise<T>.thento take(Awaited<T>) => ...rather than(T) => ...