Follow

Keep Up to Date with the Most Important News

By pressing the Subscribe button, you confirm that you have read and are agreeing to our Privacy Policy and Terms of Use
Contact

Building a better Promise.all, or, is it possible to infer and unbox generics in a TS object passed to a function at compile time?

I’m working on building a better implementation of Promise.all which takes an object of string keys and Promises. The goal is that the result of a successful run will map the Promise results to their corresponding keys while preserving the underlying type information:

// compiles and properly infers result type
const result: number = await resolveAll({
  aNumber: Promise.resolve(1),
  anObject: Promise.resolve({foo: 1, bar: "baz"}),
}).then(({ 
  aNumber,            // typeof number 
  anObject: { foo }   // typeof {foo: number, bar: string}
}) => aNumber + foo)  

My question is whether or not it is possible to define resolveAll and the corresponding object it returns (ostensibly one which exposes a then method) such that they preserve the type information of the generic types encapsulated by the Promise values passed in as an argument.

It seems as though a solution to this problem would necessitate Rank-2 Types (What is the purpose of Rank2Types?) but I’m sufficiently out of my depth in Typescript that I’m not sure whether or not this is possible using an existing feature of the type system.

MEDevel.com: Open-source for Healthcare and Education

Collecting and validating open-source software for healthcare, education, enterprise, development, medical imaging, medical records, and digital pathology.

Visit Medevel

Thanks in advance for the help!

>Solution :

There is no need for any complicated magic, just a generic parameter and the built-in Awaited utility.

function resolveAll<T extends Record<any, Promise<any>>>(
  t: T
): Promise<{
  [K in keyof T]: Awaited<T[K]>;
}> {
  const entries = Object.entries(t);
  return Promise.all(entries.map(([_, v]) => v)).then((res) =>
    entries.reduce(
      (acc, [k, _], i) => ({
        ...acc,
        [k]: res[i],
      }),
      {} as { [K in keyof T]: Awaited<T[K]> }
    )
  );
}

Playground

Add a comment

Leave a Reply

Keep Up to Date with the Most Important News

By pressing the Subscribe button, you confirm that you have read and are agreeing to our Privacy Policy and Terms of Use

Discover more from Dev solutions

Subscribe now to keep reading and get access to the full archive.

Continue reading