How I understand Covariance & Contravariance in typescript
Tags: typescript, node, javascript, webdev
Published: 2021-03-02 21:36:27

Covariance, contravariance, bivariance... Theses words seems unfamiliar, difficult to understand for you ? 

I promise you, at the end of this article, all of this will be more clearly for you.

## What is ? 

When you are using class, a class can extend to another class. For example:

```typescript
class Animal {}

class Dog extends Animal {}

class Greyhound extends Dog {}
```

That means 2 important things :

- **Dog** is a `subtype` of **Animal**, and **Animal** is the `supertype` of **Dog**.

- **Dog** is the `supertype` of **Greyhound** and **Greyhound** is a `subtype` of **Dog**

**Yes nice and ?**

We can now understand the definitions of Covariance, contravariance and bivariance !

### Covariance :

Covariance accept `subtype` but doesn't accept `supertype`

We can take a function that will accept only `covariant` type of `Dog`

```typescript
const acceptDogCovariance = function (value: Covariant<Dog>) { ... }

acceptDogCovariance(new Animal()) // Error, since Animal is a supertype of Dog
acceptDogCovariance(new Dog()) // Ok
acceptDogCovariance(new Greyhound()) // Ok since Greyhound is a subtype of Dog
```

### Contravariance :

Contravariance accept `supertype` but doesn't accept `subtype`

```typescript
const acceptDogContravariance = function (value: Contravariance<Dog>) { ... }

acceptDogContravariance(new Animal()) // Ok, since Animal is a supertype of Dog
acceptDogContravariance(new Dog()) // Ok
acceptDogContravariance(new Greyhound()) // Error since Greyhound is a subtype of Dog
```

### Bivariance :

Bivariance accept both, `supertype` & `subtype` !

So now we learn the definitions, but how it's working in Typescript ? Especially for `function`

## How Typescript use covariance and contravariance for argument in function ?

A legit question, isn't ?

In typescript, argument types are `bivariant` ! In fact this is not a `correct` behavior, but why ?

Ok ok, we will illustrate this `unsound` case !

```typescript
class Animal {
    doAnimalThing(): void {
        console.log("I am a Animal!")
    }
}

class Dog extends Animal {
    doDogThing(): void {
        console.log("I am a Dog!")
    }
}

class Cat extends Animal {
    doCatThing(): void {
        console.log("I am a Cat!")
    }
}

function makeAnimalAction(animalAction: (animal: Animal) => void) : void {
    let cat: Cat = new Cat()
    animalAction(cat)
}

function dogAction(dog: Dog) {
    dog.doDogThing()
}

makeAnimalAction(dogAction) // TS Error at compilation, since we are trying to use `doDogThing()` to a `Cat`
```
In one example we can demonstrate that Bivariance for argument type is `unsound`, but don't be sad we can fix this thanks to [Typescript 2.6](https://www.typescriptlang.org/docs/handbook/release-notes/typescript-2-6.html) you just need to use `--strictFunctionTypes` flag in your Ts config.

So `makeAnimalAction` need to be `contravariant` for argument type. Thanks to this we can avoid to make Dog action to a Cat !

```typescript
function makeAnimalAction(animalAction: (animal: Animal) => void) : void {
    let cat: Cat = new Cat()
    animalAction(cat)
}

function animalAction(animal: Animal) {
    animal.doAnimalThing()
}

makeAnimalAction(animalAction) // "I am a Animal!"
```

## How Typescript use covariance and contravariance for returned type in function ?

The returned type of a function in Typescript is `covariant` !

Thank you to read this ..... Ok ok, I will try to demonstrate it !

```typescript
class Animal {}

class Dog extends Animal {
    bark(): void {
        console.log("Bark")
    }
}

class Greyhound extends Dog {}

function makeDogBark(animalAction: (animal: Animal) => Dog) : void {
    animalAction(new Animal()).bark()
}

function animalAction(animal: Animal): Animal {
    return animal
}

makeDogBark(animalAction) // Error since not all Animal can bark.
```

Here we need to have a Dog or a `subtype` of Dog in returned type for `makeDogBark` argument. So returned type need to be `covariant`

### TL;TR & Conclusion

So in Typescript, argument type need to be `contravariant` and function types need to be `covariant` in their return types.

---

I hope you like this reading!

🎁 You can get my new book `Underrated skills in javascript, make the difference` for FREE if you follow me on [Twitter](https://twitter.com/code__oz) and MP me 😁

Or get it [HERE](https://codeoz.gumroad.com/l/RXLYp)

🎁 [MY NEWSLETTER](https://www.getrevue.co/profile/code__oz)

☕️ You can [SUPPORT MY WORKS](https://www.buymeacoffee.com/CodeoZ) 🙏

🏃‍♂️ You can follow me on 👇

🕊 Twitter : [https://twitter.com/code__oz](https://twitter.com/code__oz)

👨‍💻 Github: https://github.com/Code-Oz

And you can mark 🔖 this article!


*I use https://www.stephanboyer.com/post/132/what-are-covariance-and-contravariance in order to understand and explain this article*