Skip to content
webtype.orgwebtype.org

    ↑↓ 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.

    Follow the typeCompiler checked
    ExpressionItem<string[]>
    Resolves tostring
    ExpressionItem<number[]>
    Resolves tonumber
    ExpressionItem<boolean>
    Resolves tonever

    Match an array, capture its element type as U. A non-array does not match this pattern.

    Type definition
    type Item<T> = T extends (infer U)[] ? U : never

    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