Typescript doesn't infer types for Promise.all()

Viewed 50

Please explain someone the reason and mechanics of that TS doesn't match the types for a (string vs 'test') matching those for b at the same time. How can I solve this? The playground is here. Thank you in advance.

(async () => {
    const [a]: ['test'] = await Promise.all([Promise.resolve('test')])
    //     ^ type mismatch here
    const b: 'test' = await Promise.resolve('test')
    console.log(a, b)
})()
1 Answers

The issue here seems to be the implementation of the Promise.resolve() function. Whoever implemented the typing did not want types to be inferred as narrow as they could be.

const c = await Promise.resolve("test")
//    ^? c: string

As you can see, when calling Promise.resolve() with a string literal, the type is widened to string.

Interestingly, this does not happen when giving an explicit type to the variable.

const d: "test" = await Promise.resolve("test")
//    ^? d: "test"

This behaviour seemed to change in version 3.5 but I am still looking for the changelog which explains this feature.


So what are your options?

  1. Use as const when using Promise.resolve().
const [a1] = await Promise.all([Promise.resolve('test' as const)])
//     ^? a1: "test"
  1. You could write your own wrapper function for Promise.resolve() which respects narrow types.
type Every =
  | null
  | string
  | number
  | boolean
  | Array<Every>
  | object
  | symbol
  | undefined
  | {
      [prop: string]: Every
    }

function PromiseResolve<T extends Every>(p: T): Promise<T> {
  return Promise.resolve(p)
}


const [a2] = await Promise.all([PromiseResolve('test')])
//     ^? a2: "test"

Playground

Related