CodeBlanks Logo Icon CodeBlanks

Challenge 2: Variance

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

index.ts
// 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ır

Sample solution

index.ts
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

index.ts
// 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ğır

Sample solution

index.ts
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?

index.ts
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

Question
// T'yi parametre olarak tüketen fonksiyon
type Consumer<T> = (x: ____) => void;
Answer
// 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

All lessons in Advanced