Varianca përshkruan se si nëntipi i një lloji të përbërë lidhet me nëntipin e pjesëve të tij — dmth kur Container<Sub> mund të caktohet në Container<Super>?
Le të Dog të jetë një nëntip i Animal.
let dogs: Dog[] = [];
let animals: Animal[] = dogs; // ✅ Dog[] is assignable to Animal[]
Tipet e kthimit dhe varjet janë kovariantë: nëse Dog ⊆ Animal, atëherë Dog[] ⊆ Animal[]. Një funksion që kthen Dog mund të përdoret ku pritet një që kthen 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
Parametrat e funksionit janë kontravariantë: Handler<Animal> mund të caktohet në Handler<Dog>, e kundërta e marrëdhënies së elementeve. Kjo është e shëndoshë — diçka që trajton të gjitha kafshët sigurisht trajton qenët.
// Method parameters in TS are bivariant by default (a known unsound convenience)
interface Comparer<T> { compare(a: T): void; }
TypeScript kontrollon llojet e funksionit të vetëm kontravariantë vetëm nën strictFunctionTypes; parametrat e metodës janë me qëllim bivariantë për ergonominë, e cila teknikisht është e pabazuar.
Varianca shpjegon pse disa caktime lejohen ose refuzohen — pse Dog[] përshtatet me Animal[] por një callback (d: Dog) => void nuk mund të zëvendësojë gjithmonë (a: Animal) => void.
Kuptimi i kësaj ju ndihmon të projektoni API-je gjenerike (p.sh. pozicione vetëm-leximi vs. shkrim) dhe të dekodoni gabime të ngatërruara "not assignable" që përfshijnë funksionet dhe gjeneriken.