区别在于handler
是非方法函数,而onChange
在Props
中被声明为方法.当您启用the --strictFunctionTypes
compiler option(这是the --strict
suite of compiler functionality的一部分)时,将更严格地判断非方法函数的参数.
为了类型安全,函数的参数应该判断contravariantly,这在this q/a中有描述,但简单地说,函数参数可以安全地判断narrowed而不是widened.由于Expr | Primitive
比Expr | StringOrBool
宽,因此将handler
视为onChange
是不安全的,如添加一些功能时所示:
const SomeFunction = (_props: Props) => { _props.onChange(3) }
const handler = (el: Expr | StringOrBool) => {
if ((typeof el !== "boolean") && (typeof el !== "object")) {
console.log(el.toUpperCase()); // has to be a string, right?
}
console.log(el)
}
SomeFunction({ onChange: handler }) // runtime error: el.toUpperCase is not a function
handler
函数假定非布尔非对象参数必须是字符串,但SomeFunction
用number调用_props.onChange()
.哎呀!
所以在这一点上,应该很清楚,如果我们只关心类型安全,那么在both次调用SomeFunction
次时应该会有一个错误.实际上,如果我们将Props
重写为onChange
使用非方法语法:
interface Props {
onChange: (expr: Expr | Primitive) => void // call expression, not method
}
然后我们确实看到了两个错误:
SomeFunction({ onChange: handler }) // error!
SomeFunction({ onChange: e => handler(e) }) // error!
但是,方法(以及所有未启用--strictFunctionTypes
的函数)允许安全缩小(逆变)和不安全扩大(协方差).也就是说,bivariantly判断方法参数.为什么?事实证明,尽管这是不安全的,但它非常有用.您可以阅读this FAQ entry或this doc了解详细信息,但简而言之,它支持一些常见的编码实践(如事件处理).人们把Dog[]
当作Animal[]
来对待,尽管它在技术上是不安全的.如果治疗方法是安全的,那么Animal[]
的push()
方法的存在将阻止任何人使用Dog[]
作为Animal[]
.更多信息请参见this Q/A.
因为我们既有方法语法,也有箭头函数语法,如果我们想的话,我们可以同时使用这两种语法;只要你关心函数参数的类型安全,就不要使用方法语法.
Playground link to code