Varians beskriver hur subtypning av en sammansatt typ förhåller sig till subtypning av dess delar — dvs när är Container<Sub> tilldelningsbar till Container<Super>?
Låt Dog vara en subtyp av Animal.
let dogs: Dog[] = [];
let animals: Animal[] = dogs; // ✅ Dog[] is assignable to Animal[]
Returtyper och arrays är kovarianta: om Dog ⊆ Animal, då Dog[] ⊆ Animal[]. En funktion som returnerar Dog kan användas där en som returnerar Animal förväntas.
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
Funktionsparametrar är kontravarianta: Handler<Animal> är tilldelningsbar till Handler<Dog>, det motsatta av elementrelationen. Detta är korrekt — något som hanterar alla djur hanterar säkert hundar.
// Method parameters in TS are bivariant by default (a known unsound convenience)
interface Comparer<T> { compare(a: T): void; }
TypeScript kontrollerar fristående funktionstyper endast kontravariantly under strictFunctionTypes; metodparametrar är avsiktligt bivarianta för ergonomi, vilket är tekniskt osunt.
Varians förklarar varför vissa tilldelningar tillåts eller avvisas — varför Dog[] passar Animal[] men en (d: Dog) => void callback inte alltid kan stå in för en (a: Animal) => void.
Att förstå det hjälper dig att designa generiska API:er (t.ex. skrivskyddade jämfört med skrivpositioner) och avkoda förvirrande "inte tilldelningsbar"-fel som involverar funktioner och generiker.