跳到主要内容

函数重载

JavaScript 是一门动态语言,针对同一个函数,它可以有多种不同类型的参数与返回值,这就是函数的多态

而在 TypeScript 中,也可以相应地表达不同类型的参数和返回值的函数,如下代码所示:

function convert(x: string | number | null): string | number | -1 {
if (typeof x === "string") {
return Number(x);
}
if (typeof x === "number") {
return String(x);
}
return -1;
}
const x1 = convert("1"); // => string | number
const x2 = convert(1); // => string | number
const x3 = convert(null); // => string | number

在上述代码中,我们把 convert 函数的 string 类型的值转换为 number 类型,number 类型转换为 string 类型,而将 null 类型转换为数字 -1。此时, x1、x2、x3 的返回值类型都会被推断成 string | number 。

那么,有没有一种办法可以更精确地描述参数与返回值类型约束关系的函数类型呢?有,这就是函数重载(Function Overload),如下示例中 1~3 行定义了三种各不相同的函数类型列表,并描述了不同的参数类型对应不同的返回值类型,而从第 4 行开始才是函数的实现。

function convert(x: string): number;
function convert(x: number): string;
function convert(x: null): -1;
function convert(x: string | number | null): any {
if (typeof x === "string") {
return Number(x);
}
if (typeof x === "number") {
return String(x);
}
return -1;
}
const x1 = convert("1"); // => number
const x2 = convert(1); // => string
const x3 = convert(null); // -1

⚠️ 注意:函数重载列表的各个成员(即示例中的 1 ~ 3 行)必须是函数实现(即示例中的第 4 行)的子集,例如 “function convert(x: string): number”是“function convert(x: string | number | null): any”的子集。

在 convert 函数被调用时,TypeScript 会从上到下查找函数重载列表中与入参类型匹配的类型,并优先使用第一个匹配的重载定义。因此,我们需要把最精确的函数重载放到前面。例如我们在第 13 行传入了字符串 '1',查找到第 1 行即匹配,而第 14 行传入了数字 1,则查找到第 2 行匹配。

🌰 举个例子

interface P1 {
name: string;
}
interface P2 extends P1 {
age: number;
}
function convert(x: P1): number;
function convert(x: P2): string;
function convert(x: P1 | P2): any {}
const x1 = convert({ name: "" } as P1); // => number
const x2 = convert({ name: "", age: 18 } as P2); // number

因为 P2 继承自 P1,所以类型为 P2 的参数会和类型为 P1 的参数一样匹配到第一个函数重载,此时 x1、x2 的返回值都是 number。

function convert(x: P2): string;
function convert(x: P1): number;
function convert(x: P1 | P2): any {}
const x1 = convert({ name: "" } as P1); // => number
const x2 = convert({ name: "", age: 18 } as P2); // => string

而我们只需要将函数重载列表的顺序调换一下,类型为 P2 和 P1 的参数就可以分别匹配到正确的函数重载了。