协变(Covariance)
简单说,协变表示类型在某个方向上保持“越具体越好”的兼容性。
在 TypeScript 中,返回值类型是协变的:
1 | type Animal = { name: string } |
解释一下:
getDog
返回的是更具体的类型(Dog),赋值给返回类型是Animal
的getAnimal
是没问题的;- 反过来则不行,因为
getAnimal
可能返回一只猫,你不能假设它一定会bark()
。
这就是协变:返回值可以更具体,但不能更泛化。
逆变(Contravariance)
逆变是参数类型的行为,和协变正好相反:越泛化越安全。
1 | type Animal = { name: string } |
解释:
handleAnimal
能处理所有动物,当然也能处理狗。- 但
handleDog
只会处理会叫的狗,万一你传进去一只猫,它不会bark()
就炸了。
这就是逆变:参数类型越宽泛,越保险。
Tips
- 如果实在记不住,就这么记:返回值协变,参数逆变
- TypeScript 实际上在函数参数的逆变判断上有点“宽松”(默认是双向协变),但开启
strictFunctionTypes
后才是“标准模式” Array<T>
是协变的(因为只读),但T[]
是不完全协变的(因为可以写)
最后
搞懂协变/逆变,本质是在理解“类型的替换是否安全”这个事。不是晦涩的概念,而是类型系统背后的安全保障。
如果你经常写泛型函数、封装库、或者用第三方复杂类型推导,理解它们会让你更放心地“放类型飞”。