Skip to content
Normalize a sync or async callback

    ↑↓ move · ⏎ open · esc close

    Patterns

    How do I accept callbacks that return either `T` or `Promise<T>` and always expose one predictable result?

    Accept a maybe-promise, await it, and describe the flattened result with `Awaited`.

    The recipe

    type MaybePromise<T> = T | Promise<T>
    
    async function run<T>(
      task: () => MaybePromise<T>,
    ): Promise<Awaited<T>> {
      return (await task()) as Awaited<T>
    }
    
    const syncResult = run(() => 42)
    const asyncResult = run(async () => 'ready')

    The build compiles this and checks each result below.

    How it works

    1. 01
      type MaybePromise<T> = T | Promise<T>

      The callback contract permits immediate and deferred implementations.

    2. 02
      Promise<Awaited<T>>

      The public result is always asynchronous and nested promises are flattened.

    What you get

    Where it goes wrong

    The returned API is always asynchronous, even for a synchronous callback. Do not use this wrapper where callers rely on immediate exceptions or same-tick completion.

    Takeaway

    Normalize flexible inputs at the boundary so every caller consumes one stable output shape.

    See also

    Patterns