常识表明,子类型化在返回类型方面应该是协变的,但在参数类型方面应该是逆变的。因此,由于 E.f
的严格协变参数类型,应拒绝以下内容:
interface C {
f (o: C): void
}
interface D extends C {
g (): void // give D an extra service
}
class E implements C {
// implement f with a version which makes stronger assumptions
f (o: D): void {
o.g() // rely on the extra service promised by D
}
}
// E doesn't provide the service required, but E.f will accept
// an E argument as long as I invoke it via C.
var c: C = new E()
console.log('Try this: ' + c.f(c))
确实,运行程序会打印
Uncaught TypeError: o.g is not a function
所以:(1) 这里的基本原理是什么(大概有一个,但是不令人满意并且是 JavaScripty); (2) 编译器在这种情况下不能忽略警告是否有任何实际原因?
最佳答案
根据 krontogiannis ' 上面的评论,当比较函数类型时,一个可以是另一个的子类型或者因为源参数类型可以分配给对应的目标参数类型,或者因为对应的目标参数类型可分配给源参数类型。在语言规范中,这称为 function parameter bivariance .
允许双变参数类型的原因是对象是可变的,而不是对逆变的“天真”期望。在纯语言中,逆变是唯一明智的选择,但对于可变对象,协变或逆变是否有意义取决于您是从结构中读取还是写入结构。由于(目前)无法在类型系统中表达这种区别,双变性是一种合理(尽管不合理)的折衷方案。
关于TypeScript:子类型化和协变参数类型,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/39569016/