snippettypescriptCriticalCanonical
How to unwrap the type of a Promise?
Viewed 0 times
unwraphowthepromisetype
Problem
Say I have the following code:
How would I go about extracting the type of the promise result (in this simplified case a number) as its own type?
async promiseOne() {
return 1
} // => Promise
const promisedOne = promiseOne()
typeof promisedOne // => PromisedHow would I go about extracting the type of the promise result (in this simplified case a number) as its own type?
Solution
TypeScript 4.5
The
TypeScript 4.1 through 4.4
You can implement this yourself, with several possible definitions. The simplest is:
Note that this type uses
This uses a conditional type to check if
The definition used by TypeScript 4.5 (source) is more complicated than this to cover edge cases that do not apply to most use cases, but it can be dropped in to any TypeScript 4.1+ project.
TypeScript 2.8 through 4.0
Before TypeScript 4.1, the language did not have intentional support for recursive type aliases. The simple version shown above will still work, but a recursive solution requires an additional object type to trick the compiler into thinking that that the type is not recursive, and then pulling the property out that we want with an indexed access type.
This is officially not supported, but in practice, completely fine.
TypeScript before 2.8
Before TypeScript 2.8 this is not directly possible. Unwrapping a promise-like type without the conditional types introduced in 2.8 would require that the generic type be available on the object, so that indexed access types can be used to get the value.
If we limit the scope of the type to a single level of promises, and only accept promises, it is technically possible to do this in 2.8 by using declaration merging to add a property to the global
The
Awaited type is now built in to the language, so you don't need to write one yourself.type T = Awaited> // => numberTypeScript 4.1 through 4.4
You can implement this yourself, with several possible definitions. The simplest is:
type Awaited = T extends PromiseLike ? U : T
// Awaited> = numberNote that this type uses
PromiseLike rather than Promise. This is important to properly handle user-defined awaitable objects.This uses a conditional type to check if
T looks like a promise, and unwrap it if it does. However, this will improperly handle Promise>, unwrapping it to Promise. Awaiting a promise can never give a second promise, so a better definition is to recursively unwrap promises.type Awaited = T extends PromiseLike ? Awaited : T
// Awaited>> = numberThe definition used by TypeScript 4.5 (source) is more complicated than this to cover edge cases that do not apply to most use cases, but it can be dropped in to any TypeScript 4.1+ project.
TypeScript 2.8 through 4.0
Before TypeScript 4.1, the language did not have intentional support for recursive type aliases. The simple version shown above will still work, but a recursive solution requires an additional object type to trick the compiler into thinking that that the type is not recursive, and then pulling the property out that we want with an indexed access type.
type Awaited = T extends PromiseLike
? { 0: Awaited; 1: U }[U extends PromiseLike ? 0 : 1]
: TThis is officially not supported, but in practice, completely fine.
TypeScript before 2.8
Before TypeScript 2.8 this is not directly possible. Unwrapping a promise-like type without the conditional types introduced in 2.8 would require that the generic type be available on the object, so that indexed access types can be used to get the value.
If we limit the scope of the type to a single level of promises, and only accept promises, it is technically possible to do this in 2.8 by using declaration merging to add a property to the global
Promise interface.interface Promise {
__promiseValue: T
}
type Awaited> = T["__promiseValue"]
type T = Awaited> // => stringCode Snippets
type T = Awaited<Promise<PromiseLike<number>> // => numbertype Awaited<T> = T extends PromiseLike<infer U> ? U : T
// Awaited<Promise<number>> = numbertype Awaited<T> = T extends PromiseLike<infer U> ? Awaited<U> : T
// Awaited<Promise<Promise<number>>> = numbertype Awaited<T> = T extends PromiseLike<infer U>
? { 0: Awaited<U>; 1: U }[U extends PromiseLike<any> ? 0 : 1]
: Tinterface Promise<T> {
__promiseValue: T
}
type Awaited<T extends Promise<any>> = T["__promiseValue"]
type T = Awaited<Promise<string>> // => stringContext
Stack Overflow Q#48011353, score: 474
Revisions (0)
No revisions yet.