How to Unwrap Type of a Promise

As of Typescript 2.8, this is now possible by taking advantage of conditional types, here's a basic example: Playground

function promiseOne() {
  return Promise.resolve(1)
}
    
const promisedOne = promiseOne()
    
// note PromiseLike instead of Promise, this lets it work on any thenable
type ThenArg<T> = T extends PromiseLike<infer U> ? U : T
    
type PromiseOneThenArg = ThenArg<typeof promisedOne> // => number
// or
type PromiseOneThenArg2 = ThenArg<ReturnType<typeof promiseOne>> // => number

This isn't a perfect solution. Promises cannot contain other promises. Promise<Promise<string>> isn't real, and the type you get if you await it should be string, but ThenArg will give Promise<string>.

TypeScript 4.1 includes support for recursive type aliases, which makes it possible to write the following. Playground

type ThenArgRecursive<T> = T extends PromiseLike<infer U> ? ThenArgRecursive<U> : T

type Str = ThenArgRecursive<Promise<Promise<Promise<string>>>> // string

For older versions of TypeScript, which ban recursive types, you can work around this issue by using a recursive type alias, but be aware that this is officially not supported in those versions (though practically, usually fine). Playground

type ThenArgRecursive<T> = T extends PromiseLike<infer U>
  ? { 0: ThenArgRecursive<U>; 1: U }[U extends PromiseLike<any> ? 0 : 1]
  : T

type Str = ThenArgRecursive<Promise<Promise<Promise<string>>>> // string

An old question, but I'd like to add my 2 cents. Unwrapping a promise using the Promise type is handy, but what I found is that I usually was interested in figuring out the value of a "thenable" object, like what the await operator does.

For this, I wrote an Await<T> helper, which unwraps promises as well as thenable objects:

type Await<T> = T extends {
    then(onfulfilled?: (value: infer U) => unknown): unknown;
} ? U : T;

Use it like so:

const testPromise = async () => 42
type t1 = Await<ReturnType<typeof testPromise>>
// t1 => number

First of all, TypeScript 4.1 release notes have this snippet:

type Awaited<T> = T extends PromiseLike<infer U> ? Awaited<U> : T;

which allows you to resolve things like const x = (url: string) => fetch(url).then(x => x.json()), but combining answer from Jon Jaques with comment from ErikE and given that you use version older than 4.1 we get:

type Await<T> = T extends PromiseLike<infer U> ? U : T

and example:

const testPromise = async () => 42
type t1 = Await<ReturnType<typeof testPromise>>
// t1 => number

Tags:

Typescript