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
- A producer is covariant:
out. - A consumer is contravariant:
in. - Method parameters are checked bivariantly, which is a known hole.
Keywords and builtins used here
AnimalConsumerDogProducerTconstextendsinterfacestringtype
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.
Step 4 of 4 in Type parameters, step 4 of 20 in Generics.