编程前端/后端开发人员

严格函数类型选项在TypeScript中的结构和工作原理是什么?它如何影响函数的协变和逆变类型检查,在什么情况下签名不匹配会导致编译错误?

用 Hintsage AI 助手通过面试

答案。

问题背景

默认情况下,TypeScript在函数类型签名的匹配方面允许一定的“宽松性”,允许可逆和不可逆的函数被视为兼容。从TypeScript 2.6开始,引入了strictFunctionTypes选项,确保对函数类型进行严格检查,并防止许多类型错误,尤其是在大型代码库中。

问题

在没有严格检查的情况下,可能会出现函数处理程序或回调接受更多或更具体的参数类型,而对此开发者并不知情。这会导致与返回类型的协变性和参数的逆变性相关的运行时错误。

解决方案

strictFunctionTypes选项为函数参数类型引入了严格的逆变性。现在,只有当源类型的参数是目标参数的超类型时,函数才被认为是兼容的,而反之则不然。

代码示例:

type Animal = { name: string }; type Cat = { name: string; meow: () => void }; let animalHandler: (a: Animal) => void; let catHandler: (c: Cat) => void; animalHandler = catHandler; // 在strictFunctionTypes下会报错:参数过于具体 catHandler = animalHandler; // 允许,Cat是Animal的子类型

关键特性:

  • 函数参数在“超类型”兼容性方面进行检查(逆变性)
  • 返回值在协变性方面进行检查(允许子类型)
  • 签名不匹配会导致编译错误

陷阱问题。

在strictFunctionTypes出现之前,是否可以将更具体类型的处理程序分配给函数?

是的,在没有激活strictFunctionTypes之前,TypeScript允许将更具体的函数分配给通用函数,这会导致运行时问题:

enum E { A, B } const f: (e: E) => void = (e: E.A) => {} // 在没有strictFunctionTypes下:允许

strictFunctionTypes如何影响具有可选参数的回调?

如果函数回调的参数使某些参数成为可选参数,则严格检查将不允许在期待多个必填参数的地方使用少量必填参数的函数。这可以防止callback未获得所需数据的情况。

在旧项目中启用strictFunctionTypes会出现兼容性问题吗?

是的,存在出现新编译错误的风险,因为许多函数和处理程序可能以逆变性不匹配的方式相互分配。这通常出现在回调或使用第三方库API而没有严格类型时。

常见错误和反模式

  • 使用过时的回调类型而没有严格参数检查
  • 尝试将更具体/狭窄的参数类型的函数分配给更通用的处理程序
  • 在启用strictFunctionTypes时忽略错误(注释掉选项而不是修复类型)

生活中的示例

消极案例

在一个大型项目中,事件处理程序接受更具体的类型(MouseEvent而不是通用Event)。在启用严格选项之前,这并不会被发现,导致在与不同事件源运行时出现错误。

优点:

  • 更快的原型设计

缺点:

  • 事件类型不匹配时出现运行时错误
  • 扩展代码后的调试困难

积极案例

该项目一开始就使用strictFunctionTypes。在添加新的处理程序时,所有类型之间的不一致都会自动被编译器发现。代码对于拼写错误变得更稳定,维护起来更简单。

优点:

  • 可靠性
  • 函数和处理程序传递的安全性
  • 重新构建时的可预测行为

缺点:

  • 需要仔细设计签名
  • 在某些情况下需要编写额外的包装或重载以实现兼容性