当前位置:  首页>> 技术小册>> TypeScript入门指南

TypeScript 中,函数类型有着协变和逆变两种关系。这两种关系是非常重要的概念,尤其是在设计接口和泛型的时候,深入理解它们可以帮助我们更好地使用 TypeScript。本章节将会介绍协变和逆变的概念,并通过代码示例来展示它们的应用。


1、协变

协变是指一个类型 S 可以被赋值给另一个类型 T,如果 S 比 T 更具体(更窄)。这个概念在函数类型中的应用,可以理解为函数参数的类型可以是参数类型的子类型。换句话说,如果我们将一个接受类型为 Animal 的函数赋值给一个接受类型为 Cat 的函数,那么就是在进行协变。

我们可以通过下面的代码示例来展示协变:

  1. interface Animal {
  2. name: string;
  3. }
  4. interface Cat extends Animal {
  5. meow(): void;
  6. }
  7. function takeAnimal(animal: Animal) {
  8. console.log(`Taking care of ${animal.name}`);
  9. }
  10. function takeCat(cat: Cat) {
  11. console.log(`Taking care of ${cat.name}`);
  12. cat.meow();
  13. }
  14. takeAnimal = takeCat; // 协变

在上面的代码中,我们定义了两个函数 takeAnimal 和 takeCat,它们接受不同的参数类型。takeAnimal 接受类型为 Animal 的参数,而 takeCat 接受类型为 Cat 的参数。然后,我们将 takeCat 赋值给 takeAnimal,这是一种协变的情况。因为 Cat 是 Animal 的子类型,所以接受 Cat 的函数可以被用作接受 Animal 的函数。

2、逆变

逆变是指一个类型 S 可以被赋值给另一个类型 T,如果 S 比 T 更加抽象(更宽)。在函数类型中的应用,可以理解为函数参数的类型可以是参数类型的超类型。换句话说,如果我们将一个接受类型为 Cat 的函数赋值给一个接受类型为 Animal 的函数,那么就是在进行逆变。

我们可以通过下面的代码示例来展示逆变:

  1. interface Animal {
  2. name: string;
  3. }
  4. interface Cat extends Animal {
  5. meow(): void;
  6. }
  7. function takeAnimal(animal: Animal) {
  8. console.log(`Taking care of ${animal.name}`);
  9. }
  10. function takeCat(cat: Cat) {
  11. console.log(`Taking care of ${cat.name}`);
  12. cat.meow();
  13. }
  14. takeCat = takeAnimal; // 逆变

在上面的代码中,我们将 takeAnimal 赋值给 takeCat,这是一种逆变的情况。因为 Animal 是 Cat 的超类型,所以接受 Animal 的函数可以被用作接受 Cat 的函数。

3、逆变和协变在泛型中的应用

逆变和协变不仅仅适用于函数类型,它们还可以应用于泛型类型。我们可以使用 in 表示逆变,使用 out 表示协变。在 TypeScript 中,逆变和协变在泛型中的应用是非常常见的,它们可以帮助我们更好地定义通用的接口和函数。

我们可以通过下面的代码示例来展示泛型中的逆变和协变:

  1. interface Animal {
  2. name: string;
  3. }
  4. interface Cat extends Animal {
  5. meow(): void;
  6. }
  7. interface Box<T> {
  8. value: T;
  9. }
  10. let animalBox: Box<Animal> = { value: { name: "Fluffy" } };
  11. let catBox: Box<Cat> = { value: { name: "Whiskers", meow() {} } };
  12. function takeAnimalBox(box: Box<Animal>) {
  13. console.log(`Taking care of ${box.value.name}`);
  14. }
  15. function takeCatBox(box: Box<Cat>) {
  16. console.log(`Taking care of ${box.value.name}`);
  17. box.value.meow();
  18. }
  19. takeAnimalBox = takeCatBox; // 逆变
  20. catBox = animalBox; // 协变

在上面的代码中,我们定义了一个泛型类型 Box,它有一个 value 属性,类型为 T。然后,我们定义了两个函数 takeAnimalBox 和 takeCatBox,它们接受 Box 类型的参数。最后,我们将 takeCatBox 赋值给 takeAnimalBox,这是一种逆变的情况。同时,我们也将 animalBox 赋值给 catBox,这是一种协变的情况。

小结
在 TypeScript 中,函数类型有着协变和逆变两种关系,这两种关系是非常重要的概念。协变和逆变可以应用于函数类型和泛型类型,帮助我们更好地定义通用的接口和函数。理解协变和逆变的概念可以帮助我们更好地使用 TypeScript,并避免一些潜在的错误。


该分类下的相关小册推荐: