编程全栈开发者

如何实现和正确类型化带有可选参数或默认值的回调函数?可能会出现哪些困难以及如何避免?

用 Hintsage AI 助手通过面试

回答。

问题背景:

回调函数是 JavaScript 和 TypeScript 中异步编程和委托责任的常见做法。在 TypeScript 中,对这些函数的类型化是确保安全性的重要部分,尤其是当参数可能是可选的或具有默认值时。

问题:

在动态的 JavaScript 中,缺乏对回调参数的类型化会导致值传递错误、与 undefined 混淆以及参数顺序错误。在 TypeScript 中,类型化是避免这类问题的必要步骤,但在外部很难遵循与可选性、顺序和默认值相关的所有细节。

解决方案:

明确指定所有参数的类型,用问号表示可选参数,并在函数声明时直接指定默认值,确保在类型中正确描述它们。

代码示例:

function fetchData( url: string, callback: (data: any, error?: Error) => void ) { // ... } // 带有可选参数 error 的回调 fetchData('/api', (data, error) => { if (error) { // 处理 } else { // 成功 } }); // 带有默认参数的回调 function process( cb: (x: number, y?: number) => void = (x, y = 10) => { /* ... */ } ) { /* ... */ }

关键特点:

  • 可选参数使用“?”符号。
  • 默认值在函数定义中指定。
  • 类型化使得代码的维护更简单,减少错误。

有陷阱的问题。

回调的消费者是否需要明确考虑所有参数,包括可选参数?

不需要,他们可以省略可选参数,TypeScript 不会报错 — 处理是正确的,因为使用了问号语法。

可以将回调的第一个参数设为可选,第二个参数设为必需吗?

不可以。可选参数必须始终在参数列表的最后。打破这一顺序会导致类型错误。

如果不标明参数的可选性,但又未在调用时传递该参数,会发生什么?

TypeScript 会报错 — 如果参数是必需的,必须传递。只有可选或默认参数可以被省略。

常见错误和反模式

  • 可选参数和必需参数的顺序错误(必需参数在前,可选参数在后)。
  • 未明确指明类型或使用 “any” — 会导致 TypeScript 的优势丧失。
  • 试图传递 undefined 作为值并等待默认值触发(仅在缺少参数时触发)。

真实案例

消极案例

将回调的第二个参数标记为必需,但在调用时未传递。导致编译错误。

优点:

  • 代码编写快速简单,无需详细说明。

缺点:

  • 无法编译。
  • 灵活性降低,责任混淆。

积极案例

对带有可选参数的回调进行类型化:

(cb: (x: number, y?: number) => void)

或指定默认值:

f = (x: number, y = 10) => { ... }

优点:

  • 调用回调时没有错误的保证。
  • 可读性和可维护性。

缺点:

  • 需要注意参数的顺序。