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.