La variance décrit comment la sous-typage d'un type composite se rapporte à la sous-typage de ses parties — c'est-à-dire quand Container<Sub> est-il assignable à Container<Super>?
Soit Dog un sous-type de Animal.
La variance décrit comment la sous-typage d'un type composite se rapporte à la sous-typage de ses parties — c'est-à-dire quand Container<Sub> est-il assignable à Container<Super>?
Soit Dog un sous-type de Animal.
let dogs: Dog[] = [];
let animals: Animal[] = dogs; // ✅ Dog[] is assignable to Animal[]
Les types de retour et les tableaux sont covariants : si Dog ⊆ Animal, alors Dog[] ⊆ Animal[]. Une fonction retournant Dog est utilisable là où on attend une retournant Animal.
type Handler<T> = (arg: T) => void;
let animalHandler: Handler<Animal> = (a) => {};
let dogHandler: Handler<Dog> = animalHandler; // ✅ (with strictFunctionTypes)
// a handler that accepts ANY Animal can safely handle a Dog
Les paramètres de fonction sont contravariants : Handler<Animal> est assignable à Handler<Dog>, l'inverse de la relation d'élément. C'est correct — quelque chose qui gère tous les animaux gère certainement les chiens.
// Method parameters in TS are bivariant by default (a known unsound convenience)
interface Comparer<T> { compare(a: T): void; }
TypeScript vérifie les types de fonction autonomes de manière contravariante uniquement sous strictFunctionTypes ; les paramètres de méthode sont intentionnellement bivariants pour l'ergonomie, ce qui est techniquement non sécurisé.
La variance explique pourquoi certaines assignations sont autorisées ou rejetées — pourquoi Dog[] s'adapte à Animal[] mais un callback (d: Dog) => void ne peut pas toujours remplacer (a: Animal) => void.