Wariancja opisuje, jak podtypowanie typu złożonego odnosi się do podtypowania jego części — tzn. kiedy Container<Sub> można przypisać do Container<Super>?
Niech Dog będzie podtypem Animal.
Wariancja opisuje, jak podtypowanie typu złożonego odnosi się do podtypowania jego części — tzn. kiedy Container<Sub> można przypisać do Container<Super>?
Niech Dog będzie podtypem Animal.
let dogs: Dog[] = [];
let animals: Animal[] = dogs; // ✅ Dog[] is assignable to Animal[]
Typy zwracane i tablice są kowariancjne: jeśli Dog ⊆ Animal, to Dog[] ⊆ Animal[]. Funkcja zwracająca Dog może być używana tam, gdzie oczekuje się funkcji zwracającej 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
Parametry funkcji są kontrawariancjne: Handler<Animal> można przypisać do Handler<Dog>, co jest odwrotnością relacji elementów. To jest uzasadnione — coś, co obsługuje wszystkie zwierzęta, na pewno obsługuje psy.
// Method parameters in TS are bivariant by default (a known unsound convenience)
interface Comparer<T> { compare(a: T): void; }
TypeScript sprawdza typy funkcji autonomicznych kontrawariantnie tylko w ramach strictFunctionTypes; parametry metod są celowo biwariantne ze względu na ergonomię, co technicznie jest niezasadne.
Wariancja wyjaśnia dlaczego pewne przypisania są dozwolone lub odrzucane — dlaczego Dog[] pasuje do Animal[], ale callback (d: Dog) => void nie zawsze może zastąpić (a: Animal) => void.
Zrozumienie tego pomaga w projektowaniu ogólnych API (np. pozycji tylko do odczytu vs. do zapisu) i dekodowaniu mylących błędów "nie można przypisać" dotyczących funkcji i typów ogólnych.
Biblioteka pytań rekrutacyjnych IT ze szczegółowymi odpowiedziami — od Juniora do Seniora.
Wesprzyj