typestar

Variance in TypeScript

Where a subtype is accepted, and the annotations that say so.

interface Animal {
  name: string;
}
interface Dog extends Animal {
  breed: string;
}

type Producer<out T> = () => T;
type Consumer<in T> = (value: T) => void;

const dogProducer: Producer<Dog> = () => ({ name: "rex", breed: "lab" });
const animalProducer: Producer<Animal> = dogProducer; // covariant

const animalConsumer: Consumer<Animal> = (animal) => console.log(animal.name);
const dogConsumer: Consumer<Dog> = animalConsumer; // contravariant

const dogs: Dog[] = [{ name: "rex", breed: "lab" }];
const animals: Animal[] = dogs; // arrays are covariant, and unsound
console.log(animalProducer().name, animals.length);
dogConsumer({ name: "fido", breed: "corgi" });

How it works

  1. A producer is covariant: out.
  2. A consumer is contravariant: in.
  3. Method parameters are checked bivariantly, which is a known hole.

Keywords and builtins used here

The run, in numbers

Lines
20
Characters to type
669
Tokens
168
Three-star pace
100 tpm

At the three-star pace of 100 tokens a minute, this run takes about 101 seconds.

Type this snippet

Step 4 of 4 in Type parameters, step 4 of 20 in Generics.

← Previous Next →