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.
Thanks in advance for the help!
CodePudding user response:
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]> }
)
);
}