When "any type" is too broad
Inside firstOf<T>, you could not do much with a T: it might be anything, so the compiler allows almost nothing on it. Suppose you want the longer of two values by their .length. Plain <T> fails, a.length is not known to exist.
A constraint narrows which types are allowed to fill the slot:
function longest<T extends { length: number }>(a: T, b: T): T { return a.length >= b.length ? a : b; }
T extends { length: number } means "any type that has a numeric length property". Strings qualify, arrays qualify, { length: 5 } qualifies, numbers do not. Inside the body .length is now safe, and each call still returns the precise input type, not a vague "something with length".
Code exercise · typescript
Run it, strings and arrays both satisfy the constraint. Then try longest(10, 20) and read the error, numbers have no length.
Code exercise · typescript
Constraints shine on arrays of objects. Any element type with a numeric id satisfies T extends { id: number } — extra properties are welcome, that is structural typing from lesson 3-1. Run it, then try ids([{ name: "pen" }]) and read the error about the missing id.
Quiz
In function pick<T extends { id: number }>(items: T[]): T, which argument is rejected?
Code exercise · typescript
Your turn. Write describeLength<T extends { length: number }>(value: T): string returning "length " + value.length. Print it for the string "typescript" and for the array [true, false].