Sur un <T> libre, on ne peut presque rien faire. Et c'est correct.On a free <T>, you can do almost nothing. And that's correct.
Le numéro précédent faisait voyager le type ; celui-ci veut s'en servir. Et là, surprise : un T totalement libre interdit d'appeler .length ou de lire .name — parce qu'on ignore tout de ses capacités. Le réflexe paresseux est de retomber dans any (et de tout perdre). Le bon geste : borner le générique à exactement ce dont on a besoin — T extends { length: number } —, ce qui débloque la capacité sans sacrifier le type précis.The previous issue made the type travel; this one wants to use it. And here's the surprise: a fully free T forbids calling .length or reading .name — because you know nothing of its capabilities. The lazy reflex is to fall back to any (and lose everything). The right move: bound the generic to exactly what you need — T extends { length: number } — which unlocks the capability without sacrificing the precise type.
function longest<T>(a: T, b: T): T { return a.length >= b.length ? a : b; // ✗ 'length' n'existe pas sur le type 'T' } // un T libre n'a AUCUNE capacité connue
La contrainte n'est pas une limite imposée au générique — c'est ce qui le rend utilisable. T extends C dit « T, mais qui a au moins la forme de C » : elle ouvre la capacité, et garde le type précis intact à la sortie.The constraint isn't a limit imposed on the generic — it's what makes it usable. T extends C says "T, but with at least C's shape": it opens the capability, and keeps the precise type intact at the output.
function longest<T extends { length: number }>(a: T, b: T): T { return a.length >= b.length ? a : b; // ✓ T a AU MOINS une length } longest([1, 2], [3]); // T = number[] → renvoie number[], pas { length } longest("aaa", "bb"); // T = string → renvoie string, type exact préservé
« De quelle capacité ai-je besoin sur ce T ? Je contrains à exactement ça, pas plus. » Pas le type concret (qui tuerait la réutilisation), pas rien (qui rendrait T inutilisable) : juste la forme dont le corps se sert."Which capability do I need on this T? I constrain to exactly that, no more." Not the concrete type (which would kill reuse), not nothing (which would make T unusable): just the shape the body uses.
On pourrait écrire a: { length: number } sans générique — mais alors le retour serait { length: number }, et l'appelant perdrait que c'était un string ou un number[]. La contrainte garde le lien du Vol 5.1 : elle exige la forme à l'entrée, mais se souvient du type exact pour la sortie.You could write a: { length: number } without a generic — but then the return would be { length: number }, and the caller would lose that it was a string or a number[]. The constraint keeps Vol 5.1's link: it demands the shape at the input, but remembers the exact type for the output.