一直对typescript的逆变,协变和双向协变似懂非懂,代码来自官方文档
function combine<T>(...funcs: ((x: T) => void)[]): (x: T) => void {
return x => {
for (const f of funcs) f(x);
};
}
function animalFunc(x: Animal) {}
function dogFunc(x: Dog) {}
let combined = combine(animalFunc, dogFunc); // (x: Dog) => void
这段代码Animal是父类包含Dog,根据参数逆变应该是用范围更广的Animal代替Dog做兼容,为什么最后推断的类型却是(x: Dog) => void而不是(x: Animal) => void,
文档是这样解释:
Above, all inferences for T originate in contravariant positions, and we therefore infer the best common subtype for T. This contrasts with inferences from covariant positions, where we infer the best common supertype.
这句话我看不懂,什么是逆变的位置,什么是协变的位置,来什么T的推断来源于逆变的位置,为什么逆变的位置推断出来最好的T的公共子类型是Dog,Dog不是不能兼容Animal吗。
希望大佬们能帮我解决这个困惑,谢谢!
combine函数作用就是让传入的参数能针对一个参数同时执行。animalFunc和dogFunc能同时针对什么类型的参数执行呢?
Dog继承于Animal,即Animal有的字段Dog必有
那么animalFunc只可能访问liveType和color两个属性,这两个属性对于Animal和Dog都是有的。但dogFunc除了可能访问liveType和color两个属性,还有可能访问shape属性。那么如果combined推断是(x: Animal) => void类型的,且传入的确实就是Animal类型参数(不包含shape字段),那么dogFunc执行的时候就会读到undefined。
在扩展一下,假设有个Puppy类型,继承于Dog。有一个puppyFunc: (x: Puppy) => void。那么combine(animalFunc, dogFunc, puppyFunc)应该推断出(x: Puppy) => void。Animal、Dog和Puppy求并集得到是Puppy。为什么说是并集,传入combine的n个函数,都要访问T类型的全部或部分,即animalFunc,dogFunc和puppyFunc的参数的并集,才能保证三个函数要访问的东西都有。