Varianz beschreibt, wie sich die Subtypisierung eines zusammengesetzten Typs auf die Subtypisierung seiner Teile bezieht — d.h. wann ist Container<Sub> Container<Super> zuweisbar?
Sei Dog ein Subtyp von Animal.
let dogs: Dog[] = [];
let animals: Animal[] = dogs; // ✅ Dog[] is assignable to Animal[]
Rückgabetypen und Arrays sind kovariant: wenn Dog ⊆ Animal, dann ist Dog[] ⊆ Animal[]. Eine Funktion, die Dog zurückgibt, ist verwendbar, wo eine erwartet wird, die Animal zurückgibt.
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
Funktions parameter sind kontravariant: Handler<Animal> ist Handler<Dog> zuweisbar, das Gegenteil der Elementbeziehung. Dies ist sicher — etwas, das alle Tiere verarbeitet, verarbeitet sicherlich Hunde.
// Method parameters in TS are bivariant by default (a known unsound convenience)
interface Comparer<T> { compare(a: T): void; }
TypeScript überprüft eigenständige Funktionstypen nur unter strictFunctionTypes kontravariant; Methodenparameter sind absichtlich bivariant aus Gründen der Ergonomie, was technisch unsicher ist.
Varianz erklärt, warum bestimmte Zuweisungen erlaubt oder abgelehnt sind — warum Dog[] zu Animal[] passt, aber ein (d: Dog) => void Callback nicht immer an die Stelle einer (a: Animal) => void treten kann.