作者:dmitripavlutin 译者:后端小智 来源:dmitripavlutin 本文已收录在 GitHub 中,有完整的一线厂商笔试的考点、资料和我的系列文章。
大多数函数接受一组固定的参数。
但有些函数可以接受可变数量的参数、不同类型的参数,甚至根据调用函数的方式返回不同的类型。 为了注释此类函数,TypeScript 提供了函数重载。
1. 函数签名
我们首先考虑一个返回特定人的祝福信息的函数。
function greet(person: string): string {
return `Hello, ${person}!`;
}
复制
里面的函数接受一个字符类型的参数:人名。 调用这个函数非常简单:
greet('World'); // 'Hello, World!'
复制
如果你想让greet()函数更加灵活怎么办? 例如,让它另外接受要祝福的人的名单。
这样的函数将接受一个字符串或字符串列表作为参数并返回一个字符串或字符串列表。
如何注释这样的函数? 有两种方法。
第一种方法是通过更新参数和返回类型直接更改函数签名。 这是构造后的greet()的样子:
function greet(person: string | string[]): string | string[] {
if (typeof person === 'string') {
return `Hello, ${person}!`;
} else if (Array.isArray(person)) {
return person.map(name => `Hello, ${name}!`);
}
throw new Error('Unable to greet');
}
复制
现在我们可以通过两种方式调用greet():
greet('World'); // 'Hello, World!'
greet(['小智', '大冶']); // ['Hello, 小智!', 'Hello, 大冶!']
复制
直接更新函数签名以支持多种调用方法是一种常见的良好做法。
然而,在个别情况下,我们可能需要采用另一种方法,单独定义你的函数可以调用的所有方法。 这些技巧称为函数重载。
2. 函数重载
第二种方法是使用函数重载功能。 当函数签名相对复杂并且涉及多种类型时,我推荐这些技巧。
定义函数重载需要定义重载签名和实现签名。
重载签名定义了函数的参数和返回类型,没有函数体。 一个函数可以有多个重载签名:对应于调用该函数的不同形式。
另一方面typescript 函数手册,实现签名也具有参数类型和返回类型,以及实现函数的主体,并且只能有一个实现签名。
// 重载签名
function greet(person: string): string;
function greet(persons: string[]): string[];
// 实现签名
function greet(person: unknown): unknown {
if (typeof person === 'string') {
return `Hello, ${person}!`;
} else if (Array.isArray(person)) {
return person.map(name => `Hello, ${name}!`);
}
throw new Error('Unable to greet');
}
复制
greet() 函数有两个重载签名和一个实现签名。
每个重载签名都描述了调用该函数的方式。 就greet()函数而言,我们可以通过两种方式调用它:使用字符串参数,或使用字符串字段参数。
实现签名 functiongreet(person:unknown):unknown{...} 包含该函数如何工作的适当逻辑。
现在,和以前一样,您可以使用字符串类型或字符串列表的参数来调用greet()。
greet('World'); // 'Hello, World!'
greet(['小智', '大冶']); // ['Hello, 小智!', 'Hello, 大冶!']
复制
2.1 重载签名可调用
尽管实现签名实现了函数行为,但它不能直接调用。 只有重载的签名才是可调用的。
greet('World'); // 重载签名可调用
greet(['小智', '大冶']); // 重载签名可调用
const someValue: unknown = 'Unknown';
greet(someValue); // Implementation signature NOT callable
// 报错
No overload matches this call.
Overload 1 of 2, '(person: string): string', gave the following error.
Argument of type 'unknown' is not assignable to parameter of type 'string'.
Overload 2 of 2, '(persons: string[]): string[]', gave the following error.
Argument of type 'unknown' is not assignable to parameter of type 'string[]'.
复制
在前面的示例中,尽管实现签名接受未知参数,但不能使用未知类型的参数调用greet()函数(greet(someValue))。
2.1 实现签名必须是通用的
// 重载签名
function greet(person: string): string;
function greet(persons: string[]): string[];
// 此重载签名与其实现签名不兼容。
// 实现签名
function greet(person: unknown): string {
// ...
throw new Error('Unable to greet');
}
复制
重载的签名函数greet(person:string[]):string[]被标记为与greet(person:unknown):string不兼容。
实现签名的字符串返回类型不够通用,无法与重载签名的 string[] 返回类型兼容。
3.方法重载
事实上,对于后一种情况,函数重载适用于普通函数。而且我们还可以重载一个方法
在方法重载部分,重载签名和实现签名都是类的一部分。
例如,我们实现一个 Greeter 类,它有一个重载的greet()。
class Greeter {
message: string;
constructor(message: string) {
this.message = message;
}
// 重载签名
greet(person: string): string;
greet(persons: string[]): string[];
// 实现签名
greet(person: unknown): unknown {
if (typeof person === 'string') {
return `${this.message}, ${person}!`;
} else if (Array.isArray(person)) {
return person.map(name => `${this.message}, ${name}!`);
}
throw new Error('Unable to greet');
}
复制
Greeter 类包含greet() 重载:2 个描述如何调用技巧的重载签名,以及一个包含正确实现的实现签名
由于方法重载,我们可以通过两种方式调用 hi.greet():使用字符串或字符串列表作为参数。
const hi = new Greeter('Hi');
hi.greet('小智'); // 'Hi, 小智!'
hi.greet(['王大冶', '大冶']); // ['Hi, 王大冶!', 'Hi, 大冶!']
复制
4. 何时使用函数重载
如果使用得当,函数重载会大大降低可以多种方式调用的函数的可用性。 这在进行手动补全时非常有用:我们将在手动补全中枚举所有可能的重载记录。
但个别情况下,建议不要使用函数重载typescript 函数手册,而是使用函数签名。
例如,不要对可选参数使用函数重载:
// 不推荐做法
function myFunc(): string;
function myFunc(param1: string): string;
function myFunc(param1: string, param2: string): string;
function myFunc(...args: string[]): string {
// implementation...
}
复制
在函数签名中使用可选参数就足够了:
// 推荐做法
function myFunc(param1?: string, param2: string): string {
// implementation...
}
复制
5. 总结
TypeScript 中的函数重载让我们可以定义可以多种形式调用的函数。
使用函数重载需要定义重载签名:一组具有参数和返回类型但没有主体的函数。 该签名指示应如何调用该函数。
据了解,您必须编写函数的正确实现(实现签名):参数和返回类型以及函数体。 请注意,实现签名不可调用。
不仅是常规函数,类中的方法也可以重载。
代码部署后可能存在的Bug无法实时得知。 事后为了解决此类bug,花费了大量的时间在日志调试上。 顺便给大家推荐一款好用的bug监控工具Fundebug。
沟通
本文已收录在GitHub,有完整的测试场地、资料以及我的一线厂商笔试系列文章。
发表评论