Skip to main content

promise

UnwrapPromise, DeepAwaited, MaybePromise, PromiseAll, IsPromise.

import type {
  UnwrapPromise, DeepAwaited,
  MaybePromise, Promisable,
  PromiseAll, IsPromise,
} from '@gentleduck/ttest/promise'

Promise-shape transforms. For async-aware function utilities (AsyncReturnType, Awaitable) see async; these helpers operate on plain Promise<T> shapes.

UnwrapPromise

type UnwrapPromise<T> = T extends Promise<infer U> ? U : T

Unwrap Promise<T> to T; pass non-promises through unchanged.

type a = UnwrapPromise<Promise<number>>  // number
type b = UnwrapPromise<string>           // string
type c = UnwrapPromise<Promise<Promise<number>>>  // Promise<number>   (one level only)

Edge case: only one level is stripped. For full unwrapping use DeepAwaited.

DeepAwaited

type DeepAwaited<T> = T extends Promise<infer U> ? DeepAwaited<U> : T

Recursively unwrap nested promises.

type a = DeepAwaited<Promise<Promise<Promise<number>>>>  // number
type b = DeepAwaited<Promise<string>>                    // string
type c = DeepAwaited<number>                             // number

Equivalent in behavior to the built-in Awaited<T> for Promise shapes; use it when you want the explicit name in your docs.

MaybePromise

type MaybePromise<T> = T | Promise<T>

T or a promise of T. Use for async-or-sync APIs that accept either.

function read<T>(getter: () => MaybePromise<T>): Promise<T> {
  return Promise.resolve(getter())
}

read(() => 1)                     // ok — sync number
read(() => Promise.resolve('a'))  // ok — async string

Promisable

type Promisable<T> = MaybePromise<T>

Alias of MaybePromise for type-fest parity.

PromiseAll

type PromiseAll<T extends readonly unknown[]> = { [K in keyof T]: Awaited<T[K]> }

Resolved tuple shape for Promise.all-style inputs. Maps each element through Awaited.

type a = PromiseAll<[Promise<1>, Promise<'a'>, 3]>  // [1, 'a', 3]
type b = PromiseAll<readonly [Promise<number>, Promise<string>]>  // readonly [number, string]
// Derive the resolved tuple for a known input.
async function all<T extends readonly unknown[]>(items: [...T]): Promise<PromiseAll<T>> {
  return Promise.all(items) as Promise<PromiseAll<T>>
}

IsPromise

type IsPromise<T> = T extends Promise<unknown> ? true : false

true iff T is a Promise<unknown>.

import type { AssertTrue, AssertFalse } from '@gentleduck/ttest/assert'

type _ = [
  AssertTrue <IsPromise<Promise<number>>>,
  AssertFalse<IsPromise<number>>,
  AssertFalse<IsPromise<{ then: (cb: () => void) => void }>>,  // PromiseLike, not Promise
]

Edge case: IsPromise accepts Promise only, not PromiseLike. For thenables use IsPromiseLike.