Varianca opisuje, kako se podtipizacija sestavljenega tipa nanaša na podtipiziranje njegovih delov — tj. kdaj je Container<Sub> dodelljiv Container<Super>?
Naj bo Dog podtip od Animal.
let dogs: Dog[] = [];
let animals: Animal[] = dogs; // ✅ Dog[] is assignable to Animal[]
Tipi povratka in polja so kovariantni: če je Dog ⊆ Animal, je Dog[] ⊆ Animal[]. Funkcija, ki vrne Dog, je uporabna tam, kjer se pričakuje funkcija, ki vrne 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
Parametri funkcije so kontravariantni: Handler<Animal> je dodelljiv Handler<Dog>, kar je obratno od razmerja elementov. To je smiselno — nekaj, kar obdeluje vsa živali, zagotovo obdeluje pse.
// Method parameters in TS are bivariant by default (a known unsound convenience)
interface Comparer<T> { compare(a: T): void; }
TypeScript preveri samostojne vrste funkcij kontravarianto le pod strictFunctionTypes; parametri metod so namerno bivariantni za ergonomijo, kar je tehnično nevarno.
Varianca pojasnjuje, zakaj so določene dodelitve dovoljene ali zavrnjene — zakaj se Dog[] ujema z Animal[], vendar povratni klic (d: Dog) => void ne more vedno nadomestiti (a: Animal) => void.
Razumevanje tega vam pomaga pri oblikovanju generičnih API-jev (npr. položaji samo za branje v. pisanje) in dešifriranju zmedenih napak "not assignable", ki vključujejo funkcije in generike.