在TypeScript中,逆变(Contravariance)和协变(Covariance)是描述类型系统中子类型关系的概念,主要应用于函数参数和返回值的类型兼容性。
# 基本概念
# 子类型与超类型
- 子类型(Subtype):如果类型
A是类型B的子类型,则A可以赋值给B。 - 超类型(Supertype):如果类型
B是类型A的超类型,则B可以接受A的赋值。
例如:
type Animal = { name: string };
type Dog = { name: string; breed: string };
let animal: Animal;
let dog: Dog = { name: "Buddy", breed: "Golden Retriever" };
animal = dog; // Dog 是 Animal 的子类型
1
2
3
4
5
6
7
2
3
4
5
6
7
# 协变(Covariance)
协变是指子类型关系在复合类型中保持方向一致。例如,如果Dog是Animal的子类型,那么Array<Dog>也是Array<Animal>的子类型。
type Animal = { name: string };
type Dog = { name: string; breed: string };
let animals: Animal[];
let dogs: Dog[] = [{ name: "Buddy", breed: "Golden Retriever" }];
animals = dogs; // 协变:Dog[] 是 Animal[] 的子类型
1
2
3
4
5
6
7
2
3
4
5
6
7
应用场景
- 返回值类型:函数的返回值类型是协变的。
type Animal = { name: string };
type Dog = { name: string; breed: string };
let getAnimal: () => Animal;
let getDog: () => Dog = () => ({ name: "Buddy", breed: "Golden Retriever" });
getAnimal = getDog; // 协变:getDog 可以赋值给 getAnimal
1
2
3
4
5
6
7
2
3
4
5
6
7
# 逆变(Contravariance)
逆变是指子类型关系在复合类型中方向相反。例如,如果Dog是Animal的子类型,那么(param: Animal) => void是(param: Dog) => void的子类型。
示例
type Animal = { name: string };
type Dog = { name: string; breed: string };
const handleAnimal: (animal: Animal) => void = (animal) => console.log(animal.name);
let handleDog: (dog: Dog) => void = (dog) => console.log(dog.breed);
handleDog = handleAnimal; // 逆变:handleAnimal 可以赋值给 handleDog
1
2
3
4
5
6
7
2
3
4
5
6
7
应用场景
- 参数类型:函数的参数类型是逆变的。
# 双变(Bivariance)
双变是指类型关系既可以是协变的,也可以是逆变的。TypeScript在函数参数类型中默认支持双变(出于兼容性考虑),但可以通过配置strictFunctionTypes来禁用双变。
示例
type Animal = { name: string };
type Dog = { name: string; breed: string };
let handleAnimal: (animal: Animal) => void;
let handleDog: (dog: Dog) => void = (dog) => console.log(dog.breed);
handleAnimal = handleDog; // 双变:handleDog 可以赋值给 handleAnimal
handleDog = handleAnimal; // 双变:handleAnimal 可以赋值给 handleDog
1
2
3
4
5
6
7
8
2
3
4
5
6
7
8
总结
| 特性 | 协变(Covariance) | 逆变(Contravariance) | 双变(Bivariance) |
|---|---|---|---|
| 定义 | 子类型关系方向一致 | 子类型关系方向相反 | 子类型关系既可以是协变也可以是逆变 |
| 应用场景 | 返回值类型、数组类型 | 参数类型 | TypeScript函数参数默认行为 |
| 示例 | Dog[]是Animal[]的子类型 | (Animal) => void是(Dog) => void的子类型 | 函数参数类型默认支持双变 |
配置严格模式
在TypeScript中,可以通过tsconfig.json中的strictFunctionTypes选项来启用严格的函数类型检查,禁用双变行为:
{
"compilerOptions": {
"strictFunctionTypes": true
}
}
1
2
3
4
5
2
3
4
5
启用后,函数参数类型将只支持逆变,不符合逆变规则的赋值会报错。