Un Cat est un Animal. Mais le sous-typage a une direction dans un conteneur.A Cat is an Animal. But subtyping has a direction inside a container.
Le structurel du Vol 1 a établi que Cat est un sous-type d'Animal. Question du numéro : cette flèche se propage-t-elle aux conteneurs et aux fonctions ? L'intuition dit « oui, dans les deux sens » — et se trompe. Un Cat[] est bien un Animal[] (on peut traiter des chats comme des animaux), mais l'inverse est refusé. La relation n'est pas symétrique : elle a un sens. C'est ça, la variance — et le sens s'inverse là où on s'y attend le moins.Vol 1's structural typing established that Cat is a subtype of Animal. The issue's question: does that arrow propagate to containers and functions? Intuition says 'yes, both ways' — and is wrong. A Cat[] is indeed an Animal[] (you can treat cats as animals), but the reverse is refused. The relation isn't symmetric: it has a direction. That's variance — and the direction flips where you least expect it.
class Animal { name = "" } class Cat extends Animal { meow() {} } const cats: Cat[] = [new Cat()]; const animals: Animal[] = cats; // ✓ ce sens marche… const backToCats: Cat[] = animals; // ✗ …mais PAS celui-ci : // Type 'Animal[]' is not assignable to type 'Cat[]' (un Animal n'est pas un Cat)
Là où Cat <: Animal, le tableau préserve le sens : Cat[] <: Animal[]. C'est la covariance. Le réflexe du numéro : ne jamais supposer qu'une relation de sous-typage traverse un générique dans les deux sens.Where Cat <: Animal, the array preserves the direction: Cat[] <: Animal[]. That's covariance. The issue's reflex: never assume a subtyping relation crosses a generic both ways.
// Cat <: Animal (Cat est un sous-type d'Animal — Vol 1) // donc Cat[] <: Animal[] ✓ la flèche est PRÉSERVÉE → covariance // mais Animal[] </: Cat[] ✗ pas l'inverse const animals: Animal[] = cats; // ✓ traiter des Cat comme des Animal : toujours sûr
« Dans quel sens le sous-typage traverse-t-il ce générique ? » Sortie et lecture préservent la flèche (covariant) ; entrée et paramètre l'inversent (contravariant). Avant d'assigner un conteneur à un autre, demande-toi de quel côté la valeur est lue ou écrite."In which direction does subtyping cross this generic?" Output and read positions preserve the arrow (covariant); input and parameter positions flip it (contravariant). Before assigning one container to another, ask which side the value is read or written.
Le 5.1 fait voyager le type, le 5.2 le borne à une capacité. Restait la question fine : quand un type paramétré se glisse à la place d'un autre, quelle direction le sous-typage suit-il ? La variance répond — et révèle les deux endroits où TypeScript troque, sciemment, un peu de sûreté contre de l'ergonomie.5.1 makes the type travel, 5.2 bounds it to a capability. The subtle question remained: when one parameterized type stands in for another, which direction does subtyping follow? Variance answers — and reveals the two spots where TypeScript knowingly trades a bit of soundness for ergonomics.