Generic Constraints & Defaults

A generic constraint, written , restricts a type parameter to types that have a required shape so the function body can use those members safely, while a default type parameter, , supplies a fallback type when the caller doesn't provide or imply one.

Learn Generic Constraints & Defaults in our free TypeScript course — an interactive lesson with runnable examples, a practice exercise and a quick reference.

Part of the free TypeScript course at LearnCodingFast — hands-on lessons with examples you run in your browser, plus practice exercises and a quick quiz.

An unconstrained generic is a job posting that says "anyone may apply". You can't ask applicants to do anything specific because you don't know what they can do. A constraint is a job requirement — "must have a driving licence" ( extends {' '} ). Now you can confidently ask every applicant to drive (read .length ), and anyone without the licence is rejected at the door (a compile error). A default type is the "if unspecified, we'll assume X" line on the form — a sensible fallback so the common case needs no extra paperwork.

1.

A bare could be anything, so you can't touch any of its properties. Adding a shape promises those members exist. The function stays generic — it accepts strings, arrays, or any object with a length — yet the body can safely read .length :

2. Default Parameters & Multiple Constraints

A default type parameter is used when the caller neither specifies T explicitly nor gives an argument TypeScript can infer it from. You can also declare several type parameters, each with its own constraint, and combine constraints with defaults:

3. Constraining to keyof — a Safe Getter

The most useful constraint pairs two parameters: . The second parameter is forced to be a real key of the first, so a typo becomes a compile error and the return type T[K] is exactly the type of that property:

🎯 Your Turn

Implement the body of a function whose generic is constrained to anything with a length . Fill in the blanks and match the expected output.

Build a type-safe pluck that extracts one property from every object in an array — the runtime form of . Follow the outline, run it, and match the example output.

Practice quiz

What does '<T extends { length: number }>' promise inside the function body?

  • T is always a string
  • T is an array
  • T has a numeric 'length' property you can safely use
  • T is unknown

Answer: T has a numeric 'length' property you can safely use. The constraint guarantees the member exists, so reading a.length is provably safe.

Why can't you read a property off an unconstrained '<T>'?

  • T could be anything (e.g. a number), so no property is provably present
  • Generics are slow
  • TypeScript forbids all generics
  • It needs a default

Answer: T could be anything (e.g. a number), so no property is provably present. Without a constraint, the compiler knows nothing about T, so member access is rejected.

What is the difference between a constraint and a default type parameter?

  • They are the same
  • A default limits T; a constraint sets a fallback
  • Both supply fallbacks
  • A constraint LIMITS what T can be; a default supplies a FALLBACK when T is not given/inferred

Answer: A constraint LIMITS what T can be; a default supplies a FALLBACK when T is not given/inferred. <T extends U> restricts T; <T = U> provides a fallback only when T is not specified or inferred.

What does 'keyof T' produce?

  • The values of T
  • The union of all of T's property names
  • A new object

Answer: The union of all of T's property names. keyof T is the union of T's property name literals.

In 'function getProp<T, K extends keyof T>(obj: T, key: K)', what is the return type?

The lookup type T[K] is exactly the type of the selected property.

What does the K extends keyof T constraint catch at compile time?

  • Slow code
  • Missing return statements
  • A bad/typo key like getProp(user, "naem")
  • Circular types

Answer: A bad/typo key like getProp(user, "naem"). Passing a key that is not a real property of T becomes a compile error, not a runtime undefined.

When does a default type parameter '<T = string>' actually apply?

  • Always
  • Only when T is neither specified explicitly nor inferable from an argument
  • Only with keyof
  • Never

Answer: Only when T is neither specified explicitly nor inferable from an argument. If TypeScript can infer T from an argument, the inferred type wins over the default.

What is the return type of 'merge<A extends object, B extends object>(a: A, b: B)'?

  • object
  • A | B
  • unknown
  • A & B

Answer: A & B. The intersection A & B has all properties of both inputs, keeping the merged object fully typed.

Why declare 'T' before 'K' in '<T, K extends keyof T>'?

  • Alphabetical order
  • A parameter can only be constrained by ones declared before it
  • K is faster
  • It is optional

Answer: A parameter can only be constrained by ones declared before it. <K extends keyof T, T> errors because K references T before it is declared.

How do you both restrict a type parameter and give it a fallback?

  • <T = object extends {}>
  • <T: object | {}>
  • <T extends object = {}>
  • You cannot combine them

Answer: <T extends object = {}>. <T extends object = {}> means T must be an object and defaults to {} when not provided.