编程TypeScript 开发者

如何在 TypeScript 中按签名实现和正确键入函数的重载(overload)?为什么不能使用多个函数体,函数体内的参数类型会发生什么?请提供实际使用场景和最常见的错误。

用 Hintsage AI 助手通过面试

答案。

函数重载允许创建一个具有不同参数类型和返回值选项的函数。在其他语言中,如 C# 或 Java,可以描述多个具有相同名称但不同参数类型或数量的函数。TypeScript 通过在类型级别上的重载签名来实现这种机制,但函数体始终是单一的。

问题的历史是,在 JavaScript 中没有对类型或参数数量的重载的原生支持。随着 TypeScript 的出现,重载通过顺序声明多个函数签名和一个单一的实现来模拟,函数体内手动区分当前使用了哪种类型。

如果不注意签名的一致性,就会出现问题:在函数体内,TypeScript 会将参数赋予最通用的类型(所有参数类型的联合),程序员必须进行检查和类型保护。

解决方案:在显式签名中严格键入重载,合理使用联合类型和类型保护,正确记录行为。

代码示例:

function format(value: string): string; function format(value: number, locale: string): string; function format(value: any, locale?: string): string { if (typeof value === 'number') { return value.toLocaleString(locale); } return value.trim(); } format(' hello '); // 'hello' format(123456, 'ru-RU'); // '123 456'

关键特点:

  • 多个连续的辅助重载签名,下面有一个实现。
  • 在函数体内处理参数时使用联合类型或 any/unknown。
  • 函数体的类型比上面的签名更广泛/更通用。

带有陷阱的问题。

可以像在 C# 中那样声明多个具有不同函数体的同名函数吗?

不可以。在 TypeScript(和 JavaScript)中,实际上只有一个具有该名称的函数存在。重载仅在类型级别上对编译器有效,但只有一个函数体。

如果在重载签名后不实现具有最通用参数的签名,会发生什么?

TypeScript 会发出编译错误。始终必须有一个函数实现,其参数覆盖所有可能的重载选项。

function foo(a: string): string; function foo(a: number): number; // 缺少体 — 错误

我可以在函数体内使用特定于某个签名的属性吗?

不可以,只有在经过类型检查(类型保护)或类型断言之后才能使用。否则,TypeScript 不会允许直接使用这些属性,因为不知道当前调用的是哪种签名。

function bar(x: string): number; function bar(x: number): number; function bar(x: string | number): number { if (typeof x === 'string') return x.length; return x * 2; }

常见错误和反模式

  • 不在重载签名之后添加实现 - 编译将失败。
  • 在函数体内在没有类型检查的情况下使用特定属性。
  • 过于复杂化重载,导致代码不可读。
  • 不明确描述返回类型或在函数逻辑更改时忘记更新签名。

生活中的示例

负面案例

忘记添加通用的签名实现:

function sum(a: string, b: string): string; function sum(a: number, b: number): number; // 缺少实现 — 编译器报错

优点:

  • 描述方便的 API 的理念

缺点:

  • 代码无法编译,无法处理任意选项

正面案例

按照标准实现重载:

function sum(a: string, b: string): string; function sum(a: number, b: number): number; function sum(a: any, b: any): any { return typeof a === 'string' && typeof b === 'string' ? a + b : a + b; }

优点:

  • 所有选项都得到正确处理,类型在编译阶段可推导

缺点:

  • 函数体需要手动控制类型,不能忘记检查每个选项