Reputation: 386
I want to be able to have a mapped type that has resolved promise values.
This mapped type promisifies all the keys:
type PromisifiedKeys<T> = { [K in keyof T]: Promise<T[K]> }
I want a mapped type that un-promisifies/resolves them:
type PromisifiedKeys<T> = { [K in keyof T]: UnPromise<T[K]> }
I'm not sure how to do this.
Upvotes: 2
Views: 1147
Reputation: 24937
Since TypeScript 4.5, you can also use the Awaited utility type.
Awaited
is pretty much exactly the Unpromise
template that you are looking for.
type UnpromisifiedKeys<T> = { [K in keyof T]: Awaited<T[K]> }
Upvotes: 1
Reputation: 763
As of Typescript 2.8 there are conditional types. From the handbook:
type Unpacked<T> =
T extends (infer U)[] ? U :
T extends (...args: any[]) => infer U ? U :
T extends Promise<infer U> ? U :
T;
Usage example:
const p: Promise<string> = getData();
type ReturnDataType = Unpacked<p>;
I was able to implement this as such:
interface AsyncSupplementalData {
couponCodeData: Promise<string>;
}
interface ApplicationState {
[K in keyof AsyncSupplementalData]: Unpacked< AsyncSupplementalData[K] >
}
Upvotes: 3
Reputation: 329598
You really want mapped conditional types, which don't currently exist in TypeScript.
There are workarounds. If you're willing to pollute the global declaration of Promise<T>
with a phantom property, you can do this:
declare global {
interface Promise<T> {
"**unPromise**": T
}
}
export type UnPromise<T extends Promise<any>> = T['**unPromise**']
export type UnPromisifiedKeys<T extends { [k: string]: Promise<any> }> =
{[K in keyof T]: UnPromise<T[K]> }
And you get:
type Promised = {
foo: Promise<string>,
bar: Promise<number>
}
type UnPromised = UnPromisifiedKeys<Promised>;
// {foo: string, bar: number}
This is more or less what you asked for, but it's hacky.
Or, you could use inference from mapped types to have a function that takes an object of type PromisifiedKeys<T>
and returns a T
:
declare function unPromisifyKeys<T>(k: PromisifiedKeys<T>): T;
This represents as a function what you want to do with the types. If you have concrete things you want to pass to this function, it might be useful to you. (That is, if you were just looking for UnpromisifyKeys<T>
as a way to represent the output of a function, then this solution will work as-is). If you really need to figure out UnpromisifyKeys<T>
at the type level without a concrete T
value, then you can jump through hoops to force the compiler to infer the right type without causing too much busy work at runtime:
const unpromised = true as false || unPromisifyKeys(null! as Promised);
type Unpromised = typeof unpromised;
// {foo: string, bar: number}
This works but it is ugly. The only thing it has going for it over the first workaround is that the global definition of Promise<T>
is unchanged.
Maybe one of those workarounds is good enough for you? Hope that helps; good luck!
Upvotes: 1