函数类型
在ts中,对于一个函数的类型,它分为两部分,一部分是入参,一部分则是出参,默认情况下,
对方法:ts对入参采用的是双变模式,而对出参采用的是协变的模式
对函数:ts对入参采用的是逆变模式,而对出参采用的是协变的模式
即:
可以看到上面的代码编译器报错了,下面则没有报编译错误,但是如果你真的传一个Animal进去,那么就只能等着运行时报错吧
为什么ts会对方法有特殊处理
官方是这么解释的,简单来说就是因为历史原因不得不妥协,当然你可以开启严格模式,这样下面这种写法将会报错,配置在这里:
为什么ts要对函数参数采用逆变,而出参采用协变
逆变与协变的概念
协变
用到父类的地方,都可以用子类来替换
逆变
逆变,就是与协变相反,用到子类的地方,只能使用父类来替换
为什么出参要采用协变
其实不止是出参采用协变,除了入参,其他都是采用的协变的模式,这个很好理解,子类可以替代父类嘛,父类有的子类都有
为什么入参要采用逆变
因为对一个函数来说,入参是第三方提供给它的,是它自己在进行消费,这有别于出参,是它自身提供给第三方,由第三方来进行消费,如果采用协变,会导致运行时错误。
举个例子来说:
class Animal {
}
class Dog extends Animal {
say:()=>string;
}
const func:(val:Animal)=>Dog = (val:Dog)=>{
val.say();
return val;
};
func(new Animal());
这里我们给func赋值了一个入参是Dog的函数,如果采用协变,那么调用func(new Animal())必然会产生错误,而如果使用逆变,则在编译时就可以发现这个错误,避免了运行时错误
再举个例子:
[动物,动物].forEach(函数)
你的函数定义
函数(狗){狗.狗叫}
但是调用方,给你的参数是动物,动物不一定都会狗叫,比如人就不会,这时候就会报错了