函数重载
最近在读阮一峰
老师的 TS 文档教程,遇到了 函数重载
;然后自己摸索了一下 函数重载
相关知识,但是我想着箭头函数
也是函数,能不能做函数重载呢?然后我就试了试,并遇到了一些报错;下面是我 对 箭头函数 做 函数重载报错的处理方式;供大家参考;
因自己理解的也不是很深,有什么错误的地方还请大佬指正;万分感谢 🙏
什么是函数重载
有些函数可以接受不同类型或不同个数的参数,并且根据参数的不同,会有不同的函数行为。这种根据参数类型不同,执行不同逻辑的行为,称为
函数重载
(function overload)。
ts
reverse('abc');//cba
reverse([1,2,3]);// [3,2,1]
上面的reverse()
可以将参数颠倒输出。参数可以是字符串,也可以是数组;
这意味着,该函数内部有处理字符串和数组的两套逻辑,根据参数类型的不同,分别执行对应的逻辑。这就叫"函数重载"。
TypeScript 对于"函数重载"的类型声明方法是,逐一定义每一种情况的类型。
ts
function reverse(str: string): string;
function reverse(arr: any[]): any[];
上面示例中,分别对函数reverse()
的两种参数情况,给予了类型声明。但是,到这里还没有结束,后面还必须对函数reverse()
给予完整的类型声明。
ts
function reverse(str: string): string;
function reverse(arr: any[]): any[];
function reverse(stringOrArray: string | any[]): string | any[] {
if (typeof stringOrArray === "string")
return stringOrArray.split("").reverse().join("");
else return stringOrArray.slice().reverse();
}
有一些编程语言允许不同的函数参数,对应不同的函数实现。但是,JavaScript 函数只能有一个实现,必须在这个实现当中,处理不同的参数。因此,函数体内部就需要判断参数的类型及个数,并根据判断结果执行不同的操作。
ts
function add(x: number, y: number): number;
function add(x: any[], y: any[]): any[];
function add(x: number | any[], y: number | any[]): number | any[] {
if (typeof x === "number" && typeof y === "number") {
return x + y;
} else if (Array.isArray(x) && Array.isArray(y)) {
return [...x, ...y];
}
throw new Error("wrong parameters");
}
上面示例中,函数add()
内部使用if
代码块,分别处理参数的两种情况。
注意,重载的个别类型描述与函数的具体实现之间,不能有其他代码,否则报错。
以上就是阮老师
对函数重载
的描述以及示例;
那么看完这个示例我就想着,能不能把 函数声明式
改为箭头函数
的方式;于是我就改了改,代码如下:
ts
type Adds = {
(x: number, y: number): number;
(x: any[], y: any[]): any[];
};
const Add_: Adds = (x: number | any[], y: number | any[]): number | any[] => {
if (typeof x === 'number' && typeof y === 'number') {
return x + y;
} else if (Array.isArray(x) && Array.isArray(y)) {
return [...x, ...y];
}
throw new Error('warning parameters for add');
};
结果就报错了:
报错信息:
不能将类型"(x: number | any[], y: number | any[]) => number | any[]"分配给类型"Adds"。
不能将类型"number | any[]"分配给类型"number"。
不能将类型"any[]"分配给类型"number"。
于是我就去网上搜 相关问题 及报错信息;零零散散 不太全面;有的是说把返回值设为 any
;哎!确实不报错了哈;
ts
type Adds = {
(x: number, y: number): number;
(x: any[], y: any[]): any[];
};
const Add_: Adds = (x: number | any[], y: number | any[]): any => {
if (typeof x === 'number' && typeof y === 'number') {
return x + y;
} else if (Array.isArray(x) && Array.isArray(y)) {
return [...x, ...y];
}
throw new Error('warning parameters for add');
};
但是这样的话就失去了 ts
的意义;于是我又开始对此问题进行钻研,终让我找到了答案;
报错原因
这个错误是由于 TypeScript 在函数类型的兼容性检查中发现了不匹配的问题。在你的代码中,定义了一个类型Adds,它是一个函数类型,接受两个参数并返回一个值。但是,当你尝试将实际的函数Add_分配给Adds类型时,TypeScript 检测到了类型不匹配的情况。 具体来说,你的 Add_ 函数声明返回类型是 number | any[],而 Adds 类型的第二个签名期望返回类型是 any[]。这导致了类型不一致的错误。
说一下我对该段文字的理解:
首先就是 我定义的类型别名
type
,它是一个函数类型,接受两个参数并返回一个值。但是,当我将函数Add_
分配给Adds
类型时,TypeScript 检测到了类型不匹配的情况。 就是说 我定义的两个函数的返回值不一致,而我的add_
函数的返回值是number|any[]
; 我定义的type
第一个函数规定的返回值是number
,第二个参数的返回值是any[]
,Add_
函数声明返回类型是number | any[]
,而Adds
类型的第二个签名期望返回类型是any[]
。所以造成了类型不一致的错误;
ts
type Adds1 = { // 两个函数规定的返回值最终都是 number
(x: number, y: number): number; // 函数签名 (x: number, y: number) => number
(x: any[], y: any[]): number; // 函数签名 (x: any[], y: any[]) => number;
}
const Add_1: Adds1 = (x: number | any[], y: number | any[]) => {
//function(x: number | any[], y: number | any[]): number(Add_1函数我没有规定返回值类型,类型推断返回值的类型为 number)
if (typeof x === 'number' && typeof y === 'number') {
return x + y; // number
} else if (Array.isArray(x) && Array.isArray(y)) {
return 1; // number
}
throw new Error('warning parameters for add');
};
以上代码就不报错了,我把type
里的两个函数的定义的返回值都规定成了number
,也就是说这两个函数的签名期望得到类型都是number
,而Add_1 函数
的返回值类型也是 number
。 类型都一致了,所以不报错了;
问题解决
了解了报错原因之后,想要解决以上的错误信息也很简单,你需要确保
Add_ 函数
的返回类型与Adds
类型的每个签名相匹配。在这里,你可以使用类型断言来明确告诉 TypeScript,Add_1 函数
的返回类型是number | any[]
,而不是TypeScript 推断出的类型
。
可能解释的不是很好;
(根据自己的理解)代码演示:
ts
type Adds1 = {
(x: number, y: number): number;
(x: any[], y: any[]): number;
}
const Add_1: Adds1 = (x: number | any[], y: number | any[]) => {
if (typeof x === 'number' && typeof y === 'number') {
return x + y; // number
}
// 这里把 else if 注一下;
// else if (Array.isArray(x) && Array.isArray(y)) {
// return [...x,...y];
// }
throw new Error('warning parameters for add');
};
此时 add_1
函数的第一个判断条件成立;add_1
函数 返回值类型经过 TypeScript 的类型推断可以得出:
function(x: number | any[], y: number | any[]): number
如果提前规定了 add_1
函数的返回值的类型,如果不是 number
类型就会报错;
以下代码就是 TypeScript 类型推断的返回值类型
和 add_1 函数的返回值类型
一致,所以不会报错;
ts
type Adds1 = {
(x: number, y: number): number;
(x: any[], y: any[]): number;
}
const Add_1: Adds1 = (x: number | any[], y: number | any[]):number => {
if (typeof x === 'number' && typeof y === 'number') {
return x + y; // number
}
// else if (Array.isArray(x) && Array.isArray(y)) {
// return [...x,...y];
// }
throw new Error('warning parameters for add');
};
Add_1(1, 1);
此时 add_1
函数的第一个判断条件成立;add_1
函数 返回值类型经过 TypeScript 的类型推断可以得出:
function(x: number | any[], y: number | any[]): number
这段代码就是 TypeScript 类型推断的返回值类型
和 add_1 函数的返回值类型
不一致,导致的报错;
ts
type Adds1 = {
(x: number, y: number): number;
(x: any[], y: any[]): number;
}
const Add_1: Adds1 = (x: number | any[], y: number | any[]):number|any[] => {
if (typeof x === 'number' && typeof y === 'number') {
return x + y; // number
}
// else if (Array.isArray(x) && Array.isArray(y)) {
// return [];
// }
throw new Error('warning parameters for add');
};
也就是说 条件无论真假,类型推断的返回值一定不是 number|any[]
类型,要么 number
要么 any[]
最终代码如下:
ts
type Adds = {
(x: number, y: number): number;
(x: any[], y: any[]): any[];
}
const Add_: Adds = ((x: number | any[], y: number | any[]): number | any[] => {
if (typeof x === 'number' && typeof y === 'number') {
return x + y;
} else if (Array.isArray(x) && Array.isArray(y)) {
return [...x, ...y];
}
throw new Error('warning parameters for add');
}) as Adds;
不建议使用箭头函数做函数重载:
如果你真的需要使用箭头函数,可以考虑合并函数签名的方式,而不是使用函数重载。下面是一个使用箭头函数的例子
ts
type AType = number | any[];
type Adds = (x: AType, y: AType) => number | (AType)[];
const Add_: Adds = (x: AType, y: AType): number | (AType)[] => {
if (typeof x === 'number' && typeof y === 'number') {
return x + y;
} else if (Array.isArray(x) && Array.isArray(y)) {
return [...x, ...y];
}
throw new Error('warning parameters for add');
};
console.log(Add_([1, 2, 3], ['a', 'b', 'c']));
箭头函数不太适合函数重载,因为箭头函数无法像普通函数那样进行函数重载.
箭头函数在JavaScript中确实是一个非常方便的特性,它提供了一种简洁的语法来编写函数。然而,它们并不适合所有的场景,特别是在函数重载的情况下。
首先,箭头函数没有自己的this
值 。它们从封闭的作用域中继承this
值。这可能会导致在需要使用特定this
上下文的方法中出现问题,尤其是在构造函数中使用箭头函数时,会抛出异常,因为箭头函数不能作为构造函数。
其次,箭头函数不支持对象的arguments
对象 。在传统的函数中,可以使用arguments
对象来访问传递给函数的所有参数,但是在箭头函数中,这个对象是不可用的。
最后,箭头函数的重载解析与普通函数不同。由于箭头函数的语法限制,它们在重载时可能不会按照预期工作。例如,箭头函数体内的表达式必须用括号包裹,否则可能会导致代码逻辑错误。
综上所述,虽然箭头函数在很多情况下都非常有用,但在需要函数重载的场景中,它们的一些限制使得它们不是最佳选择。在这些情况下,使用传统的函数声明可能是更好的选择。
附:
阮一峰 TypeScript 教程 这是一篇非常好的文档;
👋 bye ~