函数重载允许创建一个具有不同参数类型和返回值选项的函数。在其他语言中,如 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'
关键特点:
可以像在 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; // 缺少实现 — 编译器报错
优点:
缺点:
按照标准实现重载:
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; }
优点:
缺点: