100% Free Forever
AI-Powered Learning
Industry Expert Content
Certificates & Badges
Learn At Your Own Pace
TypeScript

Generic Constraints in TypeScript

Learn how to restrict generic type parameters with `extends` so generic code can safely rely on specific properties or shapes.

GenericsAdvanced10 min readJul 8, 2026
Analogies

1. Introduction

By default, a generic type parameter T can be absolutely anything, which means the compiler will not let you assume T has any particular property or method. Generic constraints solve this by restricting what types are acceptable for a type parameter using the extends keyword, so you can rely on a minimum shape while still keeping the parameter generic.

🏏

Cricket analogy: Without a constraint, a generic T could be a bat, a ball, or a scoreboard — the compiler can't assume any of them has an 'average' property; T extends { average: number } guarantees at least that minimum shape while still accepting any player-like type.

2. Syntax

typescript
// Constrain T to anything with a numeric length property
function logLength<T extends { length: number }>(value: T): T {
  console.log(value.length);
  return value;
}

// Constrain T to a specific interface
interface HasId {
  id: number;
}
function printId<T extends HasId>(item: T): void {
  console.log(item.id);
}

// Constrain K to the keys of T
function getProp<T, K extends keyof T>(obj: T, key: K): T[K] {
  return obj[key];
}

// Default type parameter combined with a constraint
function createList<T extends object = {}>(items: T[] = []): T[] {
  return items;
}

3. Explanation

Generic constraints with extends restrict what types are acceptable for a type parameter — for example T extends { length: number } means only types that have a numeric length property (arrays, strings, and any custom object with length) can be passed as T. This lets the function body safely access value.length without the compiler complaining that length might not exist, while still allowing many different concrete types to satisfy the constraint.

🏏

Cricket analogy: T extends { length: number } accepts a batting lineup array or a player's name string because both have a numeric length, letting a function safely read value.length to count how many batters like Rohit Sharma's top order remain.

The most powerful constraint pattern combines keyof with generics for type-safe property access: function getProp<T, K extends keyof T>(obj: T, key: K): T[K]. Here keyof T produces a union of T's own property names, and constraining K to that union guarantees key really exists on obj — attempting to call getProp(obj, "nonexistentKey") is a compile-time error, not a runtime surprise.

🏏

Cricket analogy: function getStat<T, K extends keyof T>(player: T, stat: K): T[K] guarantees stat is a real property of a Player object like Kohli's, so calling getStat(player, "battingAverage") works but getStat(player, "golfHandicap") fails at compile time.

Constraints can also be interfaces or classes (T extends HasId), unions, or even other type parameters (function copyProps<T, U extends T>(target: T, source: U)), and can be paired with default type parameters (T extends object = {}) so callers can omit the type argument entirely for the common case.

🏏

Cricket analogy: T extends HasId could constrain a generic function to any Player or Umpire interface with an ID field, while function copyProps<T, U extends T>(target: T, source: U) ensures you only copy stats from a batter's extended profile onto their base profile, never the reverse.

Gotcha: a common misconception is that T extends { length: number } means T IS a { length: number } object — it actually means T is any type that is assignable to (a subtype of, or compatible with) that shape, so string, number[], and custom classes with a length field are all valid. Also, logLength<T extends { length: number }> does not restrict the return type to only length — the full shape of T is preserved on the way out.

4. Example

typescript
function logLength<T extends { length: number }>(value: T): T {
  console.log(`length: ${value.length}`);
  return value;
}

interface HasId {
  id: number;
}

function printId<T extends HasId>(item: T): void {
  console.log(`id: ${item.id}`);
}

function getProp<T, K extends keyof T>(obj: T, key: K): T[K] {
  return obj[key];
}

logLength("hello");        // string has .length
logLength([1, 2, 3, 4]);   // array has .length

printId({ id: 7, name: "Widget" }); // extra props are fine

const product = { id: 99, price: 250, name: "Keyboard" };
console.log(getProp(product, "price"));

// Compile errors (uncomment to see):
// logLength(42);              // number has no .length
// getProp(product, "weight"); // 'weight' is not a key of product

5. Output

text
length: 5
length: 4
id: 7
250

6. Key Takeaways

Practice what you learned

Was this page helpful?

Topics covered

#TypeScript#TypeScriptProgrammingStudyNotes#Programming#GenericConstraintsInTypeScript#Generic#Constraints#Syntax#Explanation#StudyNotes#SkillVeris