Как разбирам ковариацията и контравариацията в машинопис

Ковариантност, контравариантност, бивариантност... Тези думи изглеждат непознати, трудни за разбиране за вас?

Обещавам ви, че в края на тази статия всичко това ще бъде по-ясно за вас.

Какво е?

Когато използвате клас, един клас може да се разшири към друг клас. Например:

class Animal {}
class Dog extends Animal {}
class Greyhound extends Dog {}

Това означава 2 важни неща:

- Куче е subtypeот Животно, а Животное supertypeот Куче.

- Кучее supertypeот Хрътка, а Хрътка е subtypeот Куче.

Да, хубаво и ?

Вече можем да разберем дефинициите за ковариация, контравариантност и бивариантност!

Ковариация:

Ковариация приема subtypeно не приема supertype

Можем да вземем функция, която приема само covariantтип от Dog

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

Контравариантност:

Контравариантността приема supertypeно не приема subtype

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

двувариантност:

Бивариантността приема и двете, supertype и subtype!

Така че сега научаваме дефинициите, но как работи в Typescript? Специално за function

Как Typescript използва ковариация и контравариация за аргумент във функция?

Легитен въпрос, нали?

В машинопис типовете аргументи са bivariant! Всъщност това не е correctповедение, но защо?

Добре, добре, ще илюстрираме този нездрав случай!

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

В един пример можем да демонстрираме, че Bivariance за типа на аргумента е неправилен, но не тъгувайте, можем да поправим това благодарение на Typescript 2.6, просто трябва да използвате— strictFunctionTypes флаг във вашата Ts конфигурация.

Така че makeAnimalAction трябва да бъде контравариантен за тип аргумент. Благодарение на това можем да избегнем да правим куче действие на котка!

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!”

Как Typescript използва ковариация и контравариация за върнат тип във функция?

Върнатият тип на функция в 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.

Тук трябва да имаме Dog или подтип на Dog във върнат тип за аргумента makeDogBark. Така че върнатият тип трябва да бъде ковариантен.

TL;TR & Заключение

Така че в Typescript типовете аргументи трябва да бъдат контравариантни, а функционалните типове трябва да бъдат ковариантни в техните връщани типове.

Надявам се това четиво да ви хареса!

🎁 Можете да получите моята нова книга Недооценени умения в javascript, направете разликата БЕЗПЛАТНО, ако ме последвате в Twitter и ме запишете 😁

Или го вземете ТУК

🎁 МОЯТ БЮЛЕТИН

☕️ Можете да ПОДКРЕПИТЕ МОИТЕ РАБОТИ 🙏

🏃‍♂️ Можете да ме последвате на 👇

🕊 Туитър

👨‍💻 Github

И можете да маркирате 🔖 тази статия!

Използвам https://www.stephanboyer.com/post/132/what-are-covariance-and-contravariance, за да разбера и обясня тази статия