ვარიანტობა აღწერს, თუ როგორ უკავშირდება კომპოზიტული ტიპის ქვე-ტიპირება მის ნაწილების ქვე-ტიპირებას — ანუ როდის არის Container<Sub> მიმაჩნებული ისე, როგორც Container<Super>?
დაე Dog იყოს Animal-ის ქვე-ტიპი.
let dogs: Dog[] = [];
let animals: Animal[] = dogs; // ✅ Dog[] is assignable to Animal[]
ბრუნვის ტიპები და მასივები არის კოვარიანტული: თუ Dog ⊆ Animal, მაშინ Dog[] ⊆ Animal[]. ფუნქცია, რომელიც აბრუნებს Dog-ს, გამოსაყენებელია იქ, სადაც 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
ფუნქციის პარამეტრები არის კონტრავარიანტული: Handler<Animal> მიმაჩნებულია როგორც Handler<Dog>, რაც საპირესპირო მიმართულება არის ელემენტის ურთიერთობასთან. ეს სამართავია — რაიმე, რომელიც სამეჯებელია ყველა ცხოველის, მეჯობს, რომ სამეჯებელი იყოს კიდეც ძაღლის.
// Method parameters in TS are bivariant by default (a known unsound convenience)
interface Comparer<T> { compare(a: T): void; }
TypeScript ამოწმებს ცალკეული ფუნქციის ტიპების კონტრავარიანტობას მხოლოდ strictFunctionTypes-ის შემთხვევაში; მეთოდის პარამეტრები განზრახ არის ორმხრივი ვარიანტული ერგონომიკის გამო, რაც ტექნიკურად სამართავი არ არის.
ვარიანტობა ხსნის რატომ დაიშვება ან უარყოფილია გარკვეული მიმაჩნიებები — რატომ ღირს Dog[]-ი Animal[]-ის ადგილას, მაგრამ (d: Dog) => void callback არ შეიძლება ყოველთვის წარმოდგენილი იყოს როგორც (a: Animal) => void.
მისი გაგება დაგეხმარებათ გენერული API-ების დიზაინისას (მაგ., მხოლოდ წაკითხვადი დაწინააღმდეგ წერის პოზიციები) და გაუმკლავდეთ დამაბნეველ "არ არის მიმაჩნებული" შეცდომებს, რომლებიც დაკავშირებულია ფუნქციებთან და გენერიკებთან.