Saber2pr's Blog

类型协变与逆变

前几天写的东西莫名奇妙报错了,发现是类型检查的问题.所以抽空总结了一下函数类型的坑. 下面是自己的理解(具体的各种复杂理论咱也不说了, 来吧, 展示~):

  1. 协变:类型收敛

  2. 逆变:类型发散

场景:

type Color = {}

type Red = {
  red: any
}

let useC: (c: Color) => number
let useR: (r: Red) => number

useR = useC
useC = useR // 不报错?? 不安全 useR里是以Red类型执行的,但useC传入了Color类型
type Color = {}

type Red = {
  red: any
}

let c: Color
let r: Red

// 变量类型是协变的
c = r // Red类型收敛为Color类型
r = c // 报错

// 函数参数类型是逆变的
let useC: (c: Color) => number
let useR = (r: Red) => 0

useC = useR // 协变,类型收敛。开启strictFunctionTypes:true后将报错,变为逆变。
// useC执行传入Color类型,执行的是useR,Color发散为Red类型,发生错误。

useR = useC // 逆变,类型发散。
// useR执行传入Red类型,执行的是useC,Red类型收敛为Color类型。

// 函数返回值类型是协变的
let useC2: () => Color
let useR2: () => Red

useC2 = useR2
useR2 = useC2 // 报错

总结:

  1. 除了函数参数类型是逆变,都是协变。 将一个函数赋给另一个函数变量时,要保证参数类型发散,即比目标类型范围小。 目标函数执行时是执行的原函数,传入的参数类型会收敛为原函数参数类型。

  2. 协变表示类型收敛,即类型范围缩小或不变。逆变反之。

  3. 本质是执行时类型收敛是安全的。