typescriptpromisepromise.all

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


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)
})()

Solution

  • 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