Open 981377660LMT opened 3 months ago
所以:如果一个容器是只读的,才能协变,不然很容易就能把一些特殊的容器协变到更一般的容器,再往里面塞进不应该塞的类型;
那么对于可读又可写的类型,当然就是不变了:我们不能做出任何假定,不然有可能爆炸;
类型构造符→对输入类型是逆变的而对输出类型是协变的;
函数f 可以安全替换函数g,如果与函数g 相比,函数f 接受更一般的参数类型,返回更特化的结果类型;
这样的函数才是最厉害的!
Rust中有 lifetime,lifetime是和通常类型平行的另一套类型(另一个范畴),而Rust中的子类型就是对于lifetime而言的!
interface Animal {
name: string
}
interface Dog extends Animal {
breed: string
}
type VisitFn<E extends Animal = Animal> = (animal: E) => void
let visitDog: VisitFn = (dog: Dog): void => {}
// !不能将 “(dog: Dog) => void”分配给类型“VisitFn<Animal>”。
type BivariantVisitFn<E extends Animal = Animal> = { bivarianceHack(animal: E): void }['bivarianceHack']
let visitDog2: BivariantVisitFn = (dog: Dog): void => {}
class Zoo {
visitFn = (animal: Animal) => {}
}
class DogZoo extends Zoo {
override visitFn = (dog: Dog) => {} // 参数“dog”和“animal” 的类型不兼容。
visitFn2 = <E extends Animal>(animal: E) => {} // ok
}
直觉上来说,只要有协变就够了:
薛定谔想要一个笼子,里面装着一种动物,他不关心是什么动物(Cage),你给薛定谔一只装着猫的笼子(Cage),薛定谔把这个猫当作一种动物做实验;
也就是说在需要 Cage 的地方都可以给一个 Cage ;
然而,这显然是不对的!
考虑这样一个情况:
因为协变规则,对 capture 来说笼子是: Cage,往笼子里塞一个狗,完全没问题;
但是对于调用者来说,笼子的类型还是 Cage,这就破坏了类型安全:你接下来的代码期望这是装猫的笼子,其实里面装了一个狗!