stackademic

The leading education platform for anyone with an interest in software development.

Advanced TypeScript Generics

Constraints, defaults, inference, and variance patterns for reusable generic APIs

Overview

Once you understand basic type parameters, advanced generics let you express relationships between arguments and return values precisely. Constraints, generic defaults, keyof lookups, and inference helpers turn loose any-based code into type-safe, self-documenting APIs.

Syntax / Usage

Generic constraints (extends) restrict what a type parameter can be, while keyof and indexed access tie one parameter to another.

// Constrain T, then infer the key K from T's own keys
function getProp<T extends object, K extends keyof T>(obj: T, key: K): T[K] {
  return obj[key]
}

const user = { id: 1, name: "Ada" }
const name = getProp(user, "name") // inferred as string
// getProp(user, "email") // error: "email" is not a key of user

// Generic defaults keep call sites terse
interface Box<T = string> {
  value: T
}

const a: Box = { value: "hi" } // T defaults to string
const b: Box<number> = { value: 42 }

Examples

Merge two objects while preserving both key sets in the return type.

function merge<A extends object, B extends object>(a: A, b: B): A & B {
  return { ...a, ...b }
}

const merged = merge({ id: 1 }, { active: true })
// merged: { id: number } & { active: boolean }

Constrain a factory to types with a clone method so inference stays sound.

interface Cloneable<T> {
  clone(): T
}

function duplicate<T extends Cloneable<T>>(item: T): [T, T] {
  return [item, item.clone()]
}

Use inference in the return position to build a strongly typed pick.

function pick<T, K extends keyof T>(obj: T, keys: K[]): Pick<T, K> {
  const result = {} as Pick<T, K>
  for (const key of keys) result[key] = obj[key]
  return result
}

Common Mistakes

  • Adding a type parameter that is only used once—use the concrete type instead
  • Forgetting to constrain T before accessing properties, forcing any
  • Confusing T extends U (constraint) with a conditional type check
  • Over-constraining, which blocks legitimate callers from inferring T
  • Relying on inference when explicit annotation at the call site is clearer

See Also

generics utility-types type-guards