Skip to content
webtype.orgwebtype.org#213 unwrap-promise · par 3

    ↑↓ move · ⏎ open · esc close

    No. 213 · July 21, 2026 · Moderate

    Unwrap

    Implement `Unwrap<T>` so it strips every layer of `Promise`, however deeply nested. A non-promise passes through unchanged.

    01

    Try the puzzle yourself

    Par 3

    Puzzle

    unwrap-promise.ts
    Stroke 1 of 3Not run yet

    Replace ??? — your solution is checked against the cases below. Tab indents; press Escape then Tab to move focus out.

    Checks

    4
    • Unwrap<Promise<string>>
      string
    • Unwrap<Promise<Promise<number>>>
      number
    • Unwrap<boolean>
      boolean
    • Unwrap<Promise<Promise<Promise<null>>>>
      null

    How a check is judged Exact type equality, not assignability — an intersection is not the same as the flattened object.

    How everyone did

    Fewer than 5 people have solved this one so far. The distribution appears once there is enough of a sample to mean anything.

    Short game

    Fewest characters

    No public scores yet.

    Archive
    02

    Annotated solution

    Published July 22, 2026

    The solution

    type Unwrap<T> = T extends Promise<infer Inner> ? Unwrap<Inner> : T

    The common wrong answer

    type Unwrap<T> = T extends Promise<infer Inner> ? Inner : T

    This peels exactly one layer. `Promise<Promise<number>>` becomes `Promise<number>` and stops. The recursive call is what makes it total.

    Line by line

    1. T extends Promise<infer Inner>

      Pattern-matches `T` against the shape `Promise<something>` and binds that something to `Inner`.

    2. ? Unwrap<Inner>

      Recursion. `Inner` may itself be a promise, so the same question is asked again until the answer is no.

    3. : T

      The base case. Not a promise, so `T` is already the value type.

    Takeaway

    A conditional type that calls itself in its true branch is the type-level equivalent of a `while` loop. The false branch is the exit.

    Uses