9. 协变和逆变
约 659 字大约 2 分钟
2025-05-30
协变(Covariance) 和 逆变(Contravariance) 是类型系统中关于类型兼容性的重要概念,它们描述了复杂类型(如函数、泛型)如何基于其组成部分的类型保持兼容性。
一、概念
1、协变 (Covariance)
如果类型 Child
是类型 Parent
的子类型,那么 Container<Child>
也是 Container<Parent>
的子类型。通常和父子类型关系的方向保持一致。常见于数组、Promise、返回值类型等场景
class Animal {}
class Dog extends Animal {}
// 数组是协变的
let animals: Animal[] = [];
let dogs: Dog[] = [];
animals = dogs; // 合法,因为 Dog[] 是 Animal[] 的子类型
2、逆变 (Contravariance)
如果类型 Child
是类型 Parent
的子类型,那么 Container<Parent>
是 Container<Child>
的子类型。通常和父子类型关系的方向保持相反。常见于函数参数类型、事件处理程序等场景
type AnimalHandler = (animal: Animal) => void;
type DogHandler = (dog: Dog) => void;
// 函数参数是逆变的
let animalHandler: AnimalHandler = (animal: Animal) => {};
let dogHandler: DogHandler = (dog: Dog) => {};
dogHandler = animalHandler; // 合法,因为 AnimalHandler 是 DogHandler 的子类型
animalHandler = dogHandler; // 不合法
3、双变 (Bivariance)
双变指的是,既允许协变又允许逆变
interface EventHandler<T> {
(event: T): void;
}
// 没有 strictFunctionTypes 时,以下赋值都是合法的
let handleAnimalEvent: EventHandler<Animal> = (event: Animal) => {};
let handleDogEvent: EventHandler<Dog> = (event: Dog) => {};
handleAnimalEvent = handleDogEvent; // 合法
handleDogEvent = handleAnimalEvent; // 也合法
二、TypeScript 中的实现
1、函数类型
TypeScript 中函数参数是逆变的,而返回值是协变的:
interface Comparer<T> {
compare(a: T, b: T): number;
}
let animalComparer: Comparer<Animal>;
let dogComparer: Comparer<Dog>;
animalComparer = dogComparer; // 错误,因为函数参数是逆变的
dogComparer = animalComparer; // 合法
2、strictFunctionTypes
选项
TypeScript 2.6 引入了 strictFunctionTypes
选项:
- 启用时:函数参数类型检查是严格逆变的
- 禁用时:函数参数类型检查是双变的(既协变又逆变)
- 在没有配置
strictFunctionTypes
时,方法参数是双变的
interface EventHandler<T> {
(event: T): void;
}
// 没有 strictFunctionTypes 时,以下赋值都是合法的
let handleAnimalEvent: EventHandler<Animal> = (event: Animal) => {};
let handleDogEvent: EventHandler<Dog> = (event: Dog) => {};
handleAnimalEvent = handleDogEvent; // 合法
handleDogEvent = handleAnimalEvent; // 也合法
三、总结
理解协变和逆变有助于:
- 设计更安全的泛型接口
- 理解函数类型兼容性规则
- 处理回调函数和事件处理程序
- 构建类型安全的抽象
// 安全的设计:使用逆变参数和协变返回值
interface Transformer<Input, Output> {
transform(input: Input): Output;
}
// 这样使用时类型检查会更严格和安全
协变和逆变是类型系统的高级特性,理解它们可以帮助开发者编写更健壮的类型安全代码。
更新日志
2025/8/24 08:17
查看所有更新日志
e7112
-1于