逆变和协变是TypeScript函数赋值时自动应用的底层规则:参数类型逆变(宽泛类型可赋给具体类型),返回值类型协变(子类型可赋给父类型),二者共同作用导致常见报错,需理解本质而非依赖断言。

逆变和协变不是“用”出来的技巧,而是 TypeScript 类型系统在函数赋值时自动应用的底层规则。理解它们,才能看懂报错原因、避开陷阱,而不是靠试错或加 // @ts-ignore。
函数参数是逆变的:越宽泛的参数类型越“安全”
当你把一个函数赋值给另一个函数变量时,TypeScript 会检查参数类型是否兼容。这里不是“子类型能赋给父类型”,而是反过来:父类型(更宽泛)可以赋给子类型(更具体)——这就是逆变。
比如:
type LogPerson = (p: Person) => void-
type LogGuang = (g: Guang) => void(Guang是Person的子类型,多了hobbies) const logP: LogPerson = (p) => console.log(p.name);-
const logG: LogGuang = logP;✅ 允许 —— 因为logP接收任意Person,当然也接受Guang实例;调用时不会访问hobbies,安全。 -
const logP2: LogPerson = logG;❌ 报错 ——logG只能处理Guang,但logP2可能传入普通Person,logG里若用了g.hobbies就会崩溃。
逆变的本质是:参数类型越宽泛(如 Person),函数越“通用”;越具体(如 Guang),约束越强。赋值时,要求目标函数能“承受住”源函数的所有可能输入,所以必须用更宽的类型做目标。
函数返回值是协变的:越具体的返回值越“丰富”
返回值方向正好相反:子类型可以安全替代父类型,即协变。
type GetPerson = () => Person;type GetGuang = () => Guang;const getG: GetGuang = () => ({ name: '阿光', age: 25, hobbies: ['coding'] });-
const getP: GetPerson = getG;✅ 允许 ——getG返回的是Guang,它包含所有Person的字段,调用方按Person使用完全安全。 -
const getG2: GetGuang = getP;❌ 报错 ——getP可能返回一个没有hobbies的纯Person,赋给期望Guang的变量会导致属性缺失风险。
协变合理,因为返回值是“输出”,只要提供比预期更多的信息(子类型),使用者只取自己需要的部分(父类型字段),就不会出错。
报错常见场景与修复思路
很多函数类型赋值错误,根源在于没意识到参数逆变、返回值协变同时起作用。典型例子:
-
回调函数传参不匹配:比如
array.map((item: Dog) => item.bark())赋给期望(item: Animal) => void的回调 —— 这里参数是逆变,Dog → Animal不合法(应反过来),报错。修复:回调参数声明为Animal,或确保传入的数组元素类型与回调一致。 -
事件处理器类型冲突:React 中
onChange: (e: ChangeEvent<htmlinputelement>) => void</htmlinputelement>不能直接赋给(e: SyntheticEvent) => void的 props —— 因为ChangeEvent是SyntheticEvent的子类型,而参数需逆变,所以必须用更宽的SyntheticEvent做目标类型。 -
高阶函数泛型推导失败:如
function pipe<t u v>(f: (x: T) => U, g: (x: U) => V): (x: T) => V</t>,若传入的f返回值比U更具体(子类型),TS 默认不自动协变推导,可显式标注pipe<number string boolean>(f, g)</number>或开启strictFunctionTypes让类型更精确。
可控干预:strictFunctionTypes 和类型断言
TypeScript 默认开启 strictFunctionTypes(属于 strict 模式),强制函数参数逆变。关闭它会退化为“双向协变”,看似更宽松,实则掩盖潜在运行时错误,不推荐。
真需要绕过检查时,优先用类型守卫或重构函数签名,而非断言。例如:
- 错误写法:
const fn = someFunc as unknown as (x: Dog) => void; - 更好做法:提取公共接口,或让函数接受
Animal并内部判断if ('bark' in x),保持类型安全。


















