Skip to content
webtype.orgwebtype.org

    ↑↓ move · ⏎ open · esc close

    Concept 2 of 12

    Generic parameters

    A generic parameter turns an alias into a function that takes types and returns a type. Once you read it that way — arguments in, type out — the rest of the type system stops looking like syntax and starts looking like programming.

    Follow the typeCompiler checked
    ExpressionBox<string>
    Resolves to{ value: string }
    ExpressionBox<number>
    Resolves to{ value: number }

    Same shape, different argument. Each use substitutes its own type for T.

    Type definition
    type Box<T> = { value: T }

    Parameters, defaults and all

    type Box<T> = { value: T }
    type Boxed = Box<string>        // { value: string }
    
    // Defaults work exactly like function parameter defaults.
    type List<T, Sep extends string = ','> = { items: T[]; sep: Sep }
    type Plain = List<number>       // sep is ','

    Type parameters are positional, can have defaults, and the defaults may reference earlier parameters. Recursive type-level code leans on this constantly: an accumulator is nearly always a defaulted parameter the caller never passes.

    extends here means "must be at least"

    type Length<S extends string> = S['length']
    
    Length<'abc'>   // ok
    Length<42>      // Error: 42 does not satisfy the constraint 'string'

    In a parameter list, extends is a constraint: it restricts what may be passed. It is not inheritance, and it does not mean the parameter *is* that type — only that whatever arrives is assignable to it.

    The same keyword means something completely different inside a conditional type, where it asks a question rather than imposing a rule. Telling the two apart by position is one of the genuine hurdles in learning this.

    The common wrong answer

    // Intent: accept any object and read a key from it.
    type Get<T, K> = T[K]
    //                 ^ Error: type K cannot be used to index type T
    
    // Fixed: constrain K to the keys T actually has.
    type Get2<T, K extends keyof T> = T[K]

    Without a constraint the compiler has no reason to believe K is a valid key, so it refuses. Constraints are not decoration — they are what lets the body of a generic type do anything with its parameters.

    Exercise

    Make Box take a type parameter, so Box<string> holds a string.

    Try it

    2
    • Box<string>—
      → { value: string }
    • Box<1>—
      → { value: 1 }

    Takeaway

    Read type F<A, B> = ... as a function from types to types. extends in the parameter list is the argument check at the door.