在 TypeScript 中,类型的协变(Covariance)和逆变(Contravariance)描述的是在类型系统中,类型之间的关系如何随着类型操作(如函数参数和返回类型)的方向而改变。这些概念主要在函数类型的参数和返回值的类型关系中体现。
协变(Covariance)
协变指的是能够保持类型的赋值方向相同的能力。在 TypeScript 中,返回类型是协变的。
当类型 A 可以赋值给类型 B 时(A 是 B 的子类型),我们说 A 和 B 是协变的。
对于返回类型来说,如果函数返回类型是
T
,那么返回T
的子类型也是可以接受的。这意味着返回类型可以是派生类型(更具体的类型)。
逆变(Contravariance)
逆变是指能够反转类型的赋值方向的能力。在 TypeScript 中,函数参数的类型是逆变的。
如果类型 A 可以赋值给类型 B,那么在逆变的情况下,我们期望 B 类型的函数参数能接受 A 类型的值。
对于函数参数来说,这意味着如果你有一个参数类型为
T
的函数,你可以将这个函数赋值给参数类型为T
的父类型的函数变量。这是因为子类型拥有的所有属性和方法,父类型都有,所以从父类型到子类型的函数是安全的。
双向协变(Bivariance)
TypeScript 在处理函数参数类型时,实际上采用了一种称为双向协变(Bivariance)的策略,这意味着它同时允许协变和逆变。
这种设计选择使得 TypeScript 在函数类型的兼容性上更加灵活,但也可能导致一些非直观的行为,特别是在严格模式下。
例子
class Animal {}
class Bird extends Animal {}
let animalArray: Animal[] = [];
let birdArray: Bird[] = [];
// 协变:Bird[] 可以赋值给 Animal[],因为 Bird 是 Animal 的子类型
animalArray = birdArray;
function animalFunction(a: Animal) {}
function birdFunction(b: Bird) {}
// 逆变(在 TypeScript 中表现为双向协变):期望 Animal 类型的函数参数,可以接受 Bird 类型的函数
let fn: (a: Animal) => void = birdFunction; // 在严格模式下,这可能会引起警告或错误
在实际应用中,理解协变和逆变对于设计类型安全的接口和类库非常重要,尤其是当涉及到复杂的类型继承和接口实现时。然而,由于 TypeScript 的双向协变设计,开发者需要特别注意函数参数类型的兼容性问题。