Variația descrie cum se raportează subtiparea unui tip compus la subtiparea părților sale — adică când este Container<Sub> atribuibil lui Container<Super>?
Fie Dog un subtip al Animal.
let dogs: Dog[] = [];
let animals: Animal[] = dogs; // ✅ Dog[] is assignable to Animal[]
Tipurile returnate și arrayurile sunt covariante: dacă Dog ⊆ Animal, atunci Dog[] ⊆ Animal[]. O funcție care returnează Dog este utilizabilă acolo unde se așteaptă una care returnează 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
Parametrii funcției sunt contravarianți: Handler<Animal> este atribuibil lui Handler<Dog>, inversul relației elementelor. Aceasta este corectă — ceva care gestionează toate animalele cu siguranță gestionează câinii.
// Method parameters in TS are bivariant by default (a known unsound convenience)
interface Comparer<T> { compare(a: T): void; }
TypeScript verifică tipurile de funcție autonome contravariant doar sub strictFunctionTypes; parametrii metodelor sunt intenționat bivarianți pentru ergonomie, ceea ce din punct de vedere tehnic este nesigur.
Variația explică de ce anumite atribuiri sunt permise sau respinge — de ce Dog[] se potrivește cu Animal[] dar un callback (d: Dog) => void nu poate întotdeauna sta în locul (a: Animal) => void.
Înțelegerea acesteia vă ajută să proiectați API-uri generice (de ex. poziții doar citire vs. scriere) și să decodificați erori confuze "not assignable" care implică funcții și generice.