Skip to content
webtype.orgwebtype.orgTwelve concepts · 7/12

    ↑↓ move · ⏎ open · esc close

    Concept 7 of 12

    infer

    `infer` is pattern matching. You describe the shape you expect, put `infer X` where the interesting part sits, and the compiler solves for `X`. Most of the standard library’s extraction helpers are one conditional with one `infer` in the right place.

    It works anywhere a type can appear

    type Unwrap<T> = T extends Promise<infer Inner> ? Inner : T
    type Element<T> = T extends (infer E)[] ? E : never
    type Returns<T> = T extends (...a: never[]) => infer R ? R : never
    type Head<T> = T extends [infer H, ...unknown[]] ? H : never
    type Rest<S> = S extends `${string}-${infer R}` ? R : never

    Promises, arrays, function returns, tuple positions, string patterns — the mechanism is identical every time. Only the surrounding shape changes, which is why learning one of these teaches you all of them.

    Constraining what gets bound

    // Without the constraint, F is unknown and cannot be
    // interpolated into a template literal.
    type First<T> = T extends [infer F extends string, ...unknown[]]
      ? `${F}!`
      : never

    An `infer` can carry its own `extends` clause, which narrows what it binds and lets you use the result where a specific kind of type is required. Without it you often get `unknown`, and the error appears one line later than the cause.

    The common wrong answer

    // Intent: get the return type of any function.
    type Returns<T> = T extends () => infer R ? R : never
    
    Returns<() => string>          // string
    Returns<(a: number) => string> // never — pattern demands zero parameters
    
    // Fixed: never[] in parameter position accepts any signature,
    // because parameters are checked contravariantly.
    type Returns2<T> = T extends (...a: never[]) => infer R ? R : never

    The pattern has to be as permissive as the inputs you want to match. A zero-parameter function type only matches zero-parameter functions, so everything else silently falls into the false branch.

    Exercise

    Pull the element type out of an array type.

    Try it

    2
    • ElementOf<string[]>
      string
    • ElementOf<number>
      never

    Takeaway

    Describe the shape, mark the hole with `infer`, and let the compiler fill it. If nothing matches, the pattern is stricter than the input — not the other way round.

    Practiced in