返回

TypeScript协变、逆变、双向协变、不可变剖析,解密联合类型转交叉类型

前端

TypeScript 的类型协变、逆变和双向协变

在 TypeScript 的类型系统中,协变逆变双向协变 是三个至关重要的概念,它们了类型在不同场景下的行为,对 TypeScript 的类型推断和类型检查产生重大影响。了解这些概念将让你深入理解 TypeScript 的类型系统,并编写出更健壮和可维护的代码。

协变

协变 是指子类型可以赋值给父类型。这在 TypeScript 中非常常见。例如,如果 Animal 是一个父类,而 DogCat 是它的子类,那么我们可以将 DogCat 赋值给 Animal 类型变量。

interface Animal {
  name: string;
}

class Dog implements Animal {
  name: string;
  bark() {}
}

class Cat implements Animal {
  name: string;
  meow() {}
}

function printAnimalName(animal: Animal) {
  console.log(animal.name);
}

const dog = new Dog();
const cat = new Cat();

printAnimalName(dog); // "Dog"
printAnimalName(cat); // "Cat"

在这个例子中,DogCat 都派生自 Animal,所以我们可以将它们赋值给 Animal 类型变量。当我们调用 printAnimalName 函数时,TypeScript 会自动将 DogCat 对象转换为 Animal 类型,以便与函数参数匹配。

逆变

逆变 是指父类型可以赋值给子类型。这在 TypeScript 中不太常见,但也有其用途。例如,如果我们有一个比较器函数可以比较两个 Animal 对象的大小,那么我们可以使用 Animal 类型作为比较器函数的参数类型。

interface Animal {
  name: string;
}

class Dog implements Animal {
  name: string;
  bark() {}
}

class Cat implements Animal {
  name: string;
  meow() {}
}

function compareAnimals(a: Animal, b: Animal): number {
  if (a.name < b.name) {
    return -1;
  } else if (a.name > b.name) {
    return 1;
  } else {
    return 0;
  }
}

const dog = new Dog();
const cat = new Cat();

console.log(compareAnimals(dog, cat)); // -1
console.log(compareAnimals(cat, dog)); // 1

在这个例子中,Animal 类型用作比较器函数的参数类型。当我们调用 compareAnimals 函数时,TypeScript 会自动将 DogCat 对象转换为 Animal 类型,以便与函数参数匹配。

双向协变

双向协变 是指子类型和父类型可以相互赋值。这在 TypeScript 中很少见,只有少数类型具有这种特性。例如,Array 类型就是双向协变的。

const numbers: number[] = [1, 2, 3];
const strings: string[] = ["a", "b", "c"];

const mixedArray: (number | string)[] = numbers; // OK

在这个例子中,numbers 是一个 number[] 类型的数组,而 strings 是一个 string[] 类型的数组。我们可以将 numbers 赋值给 (number | string)[] 类型的变量 mixedArray,这是因为 number[]string[] 都是 (number | string)[] 类型的子类型。

不可变

除了协变、逆变和双向协变之外,TypeScript 中还有一个重要的概念叫不可变不可变 是指一个类型不能赋值给任何其他类型。这通常用于 primitive 类型,如 numberstringboolean

const numberValue: number = 123;

numberValue = "abc"; // 错误:不能将字符串分配给数字

在这个例子中,numberValue 被声明为 number 类型,所以我们不能将字符串 "abc" 赋值给它。

结论

协变、逆变、双向协变和不可变是 TypeScript 类型系统中的关键概念。理解这些概念可以让你更深入地理解 TypeScript 的类型推断和类型检查,编写出更健壮和可维护的代码。

常见问题解答

  1. 什么是协变?
    协变是指子类型可以赋值给父类型。

  2. 什么是逆变?
    逆变是指父类型可以赋值给子类型。

  3. 什么是双向协变?
    双向协变是指子类型和父类型可以相互赋值。

  4. 什么是不可变?
    不可变是指一个类型不能赋值给任何其他类型。

  5. 协变、逆变和双向协变有什么实际应用?
    协变和逆变用于实现多态性,双向协变用于实现泛型容器。