Challenge 2: Variance
- TypeScript course
- Advanced
- Lesson 55/57
- 6 min
Solve a task testing variance rules.
Variance: covariance and contravariance
Variance describes how parameterized types propagate subtype relationships. Producers are covariant: Producer<Dog> is assignable to Producer<Animal>. Consumers are contravariant: Consumer<Animal> is assignable to Consumer<Dog>.
type Producer<T> = () => T;
type Consumer<T> = (x: T) => void;
let pA: Producer<Animal>;
let pD: Producer<Dog>;
pA = pD; // OK (covariance)With strictFunctionTypes on, function parameters are strictly contravariant; class methods stay bivariant.
Practice Demonstrate covariance
Define an Animal class (field name = "hayvan") and Dog extends Animal. Write Producer<T> = () => T. Create a pDog: Producer<Dog> value, assign it to pAnimal: Producer<Animal> (covariance), and print pAnimal().name.
Starter code
// Producer<T> kovaryandır
// Animal sınıfı (name = "hayvan") ve Dog extends Animal
// pDog: Producer<Dog> oluştur, pAnimal: Producer<Animal>'a ata
// pAnimal() çağırıp name'i yazdırSample solution
class Animal { name = "hayvan"; }
class Dog extends Animal { bark() {} }
type Producer<T> = () => T;
const pDog: Producer<Dog> = () => new Dog();
let pAnimal: Producer<Animal>;
pAnimal = pDog;
console.log(pAnimal().name);Expected output
hayvan
Practice Demonstrate contravariance
Define an Animal class (field name = "h") and Dog extends Animal. Write Consumer<T> = (x: T) => void. Make a cAnimal: Consumer<Animal> value that does console.log("yedi " + x.name). Assign cAnimal to cDog: Consumer<Dog> (contravariance) and call cDog(new Dog()).
Starter code
// Consumer<T> kontravaryandır (strictFunctionTypes açık)
// cAnimal: Consumer<Animal>, cDog: Consumer<Dog>
// cAnimal'ı cDog'a ata, sonra cDog'u yeni bir Dog ile çağırSample solution
class Animal { name = "h"; }
class Dog extends Animal {}
type Consumer<T> = (x: T) => void;
const cAnimal: Consumer<Animal> = (x) => { console.log("yedi " + x.name); };
let cDog: Consumer<Dog>;
cDog = cAnimal;
cDog(new Dog());Expected output
yedi h
Questions and answers
Review the lesson questions together with their answers. To test yourself, solve the lesson interactively.
1 With `strictFunctionTypes` on, which assignment compiles?
type Consumer<T> = (x: T) => void;
class Animal { name = "a"; }
class Dog extends Animal {}
let cAnimal: Consumer<Animal>;
let cDog: Consumer<Dog>;- Neither compiles
- cAnimal = cDog;
- cDog = cAnimal; Correct answer
- Both cAnimal = cDog; and cDog = cAnimal;
Explanation: Consumer types are contravariant; the more general `Consumer<Animal>` is assignable to the more specific `Consumer<Dog>`, not the other way.
2 Consumer definition
// T'yi parametre olarak tüketen fonksiyon
type Consumer<T> = (x: ____) => void;
// T'yi parametre olarak tüketen fonksiyon
type Consumer<T> = (x: T) => void;
Explanation: A consumer uses its type in parameter position; `(x: T) => void` gives contravariance. `any` disables variance checking.
3 Variance concepts
| Term | Meaning |
|---|---|
Kovaryans |
subtype relation of produced value propagates |
Kontravaryans |
relation of consumed parameter reverses |
Bivaryans |
both covariance and contravariance accepted |
strictFunctionTypes |
makes function parameters strict |