如何定义函数重载?

函数重载,听起来像是从那些老派静态语言里来的概念,但在TypeScript里它活得很滋润。简单说,它允许你用一个函数名,根据不同的参数类型或数量,去干不同的事,并且返回不同的类型。这能让你的API对使用者来说更清晰、更智能。

先看看不用重载时有多别扭。比如你想写一个createDate函数,既能接收时间戳(一个number),也能接收日期字符串。你可能被迫写成这样:

function createDate(input: number | string): Date {
  if (typeof input === 'number') {
    return new Date(input);
  } else {
    return new Date(input);
  }
}

这么写,类型安全是有了,但对调用者不够友好。他们看到createDate的签名就是一个模糊的联合类型,编辑器没法根据传入的参数类型,精确提示返回值是什么。而函数重载,就是为了解决这个“调用意图不清晰”的问题。

在TypeScript里定义重载,写法很有特点:你要先写一系列“重载签名”,它们只描述函数可能的调用方式,没有具体实现。最后,再写一个“实现签名”,它必须能兼容前面所有的重载签名,并包含实际的函数体。

// 重载签名1:传入数字时间戳
function createDate(timestamp: number): Date;
// 重载签名2:传入日期字符串
function createDate(dateString: string): Date;
// 实现签名(对外不可见,但要兼容上面所有情况)
function createDate(input: number | string): Date {
  if (typeof input === 'number') {
    return new Date(input);
  } else {
    return new Date(input);
  }
}

现在,当你在代码里调用createDate(1625097600000)时,TypeScript知道你在使用第一个签名,返回值是Date。调用createDate('2023-07-01')时,它知道是第二个。编辑器给的提示会非常精准。

重载不只用于参数类型不同,参数数量不同也行。比如一个配置对象的函数:

function configure(config: string): void;
function configure(config: string, value: any): void;
function configure(config: string, value?: any): void {
  // 实现逻辑...
}

这里有几点实用的细节你得记住。第一,顺序很重要。TypeScript会从上到下尝试匹配重载签名,所以通常要把最精确的签名放在最前面,把最宽泛的(比如带联合类型的)放在后面。第二,实现签名的参数类型必须足够宽,以覆盖所有重载签名,但它在外部是不可见的,调用者看到的只有你定义的那些清晰的重载签名。第三,别滥用。如果一个函数有超过三四个重载签名,那可能意味着它的设计太复杂了,可以考虑拆分成多个函数,或者用带判别属性的对象参数来简化。

函数重载最棒的地方,在于它提升了代码的表达力和工具链的支持度。它让函数的契约变得一目了然。下次当你发现一个函数的核心逻辑相同,只是入口参数有几种明确形态时,就试试给它加上重载签名吧。

© 版权声明
THE END
喜欢就支持一下吧
点赞7 分享
评论 抢沙发

请登录后发表评论

    暂无评论内容