microsoft / TypeScript

TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
https://www.typescriptlang.org
Apache License 2.0
100.46k stars 12.42k forks source link

Promise constructor's return type doesn't unwrap nested promises #52764

Open bsimonjc opened 1 year ago

bsimonjc commented 1 year ago

Bug Report

πŸ”Ž Search Terms

promise constructor

πŸ•— Version & Regression Information

⏯ Playground Link

Playground link with relevant code

πŸ’» Code

const nestedPromise = (function() {
    return new Promise<Promise<number>>(resolve1 => {

        resolve1(new Promise<number>(resolve2 => {
            resolve2(5);
        }));

    })
})();

// according to TS, `result` should be a Promise<number>, but at runtime, it's actually just a number
nestedPromise.then(result => console.log('first', result, typeof result));
//                 ^?

// when using Promise.resolve(), the type is correct
const nestedPromiseResolve = (function() { return Promise.resolve(Promise.resolve(Promise.resolve(5)))})();
nestedPromiseResolve.then(result => console.log('second', result, typeof result));
//                        ^?

πŸ™ Actual behavior

Return type of the promise constructor doesn't unwrap nested promises

πŸ™‚ Expected behavior

The return type of the promise constructor should match the runtime behavior

The current constructor type is { new <T>(executor: (resolve: (value: T | PromiseLike<T>) => void, reject: (reason?: any) => void) => void): Promise<T>; }

Shouldn't the return type actually be Promise<Awaited<T>> like it is for Promise.resolve()? Is there something I'm missing here?

Artxe2 commented 11 months ago

This definitely seems wrong. Is there any work planned to fix this? The following behaviors that appear to be bugs are as follows.

// it's correct
const resolve: Promise<number> = Promise.resolve(Promise.resolve(Promise.resolve(0)))

async function pr<T>(v: T) {
  return v
}
// it's should be Promise<number>
const promise: Promise<Promise<Promise<number>>> = pr(pr(pr(0)))

const a: any = 0
;(a as Promise<Promise<Promise<number>>>).then(
  // value is should be number
  (value: Promise<Promise<number>>) => {}
)

Playground