函数

和JavaScript一样,TypeScript函数可以创建有名字的函数和匿名函数。

// 具名函数
function add (x, y) {
    return x + y;
}

// 匿名函数
let myAdd = function (x, y) { return x + y; }
// 表达式中的 具名函数 在javascript中存在这样一个限制,表达式中的 具名函数只识别为匿名函数 ,而忽略它的函数名
let fn = function add (x, y) { return x + y; }

为函数定义类型

function add (x: number, y: number): number {
    return x + y;
}

let myAdd = function(x: number, y: number): number { return x + y; };

注:给每个参数添加类型之后,可以不用给函数本身添加返回值类型,因为TypeScript能根据返回语句自动推断出返回值类型

函数重载

由于 JavaScript 是一个动态语言,我们通常会使用不同类型的参数来调用同一个函数,该函数会根据不同的参数而返回不同的类型的调用结果:

function add (x, y) {
    return x + y;
}

add(1, 2);   // 3
add("1", "2");  // "12"

以上代码在JS环境中运行是没有问题的,但是如果在TypeScript环境下且TypeScript编译器开启noImplicitAny(将TypeScript从可选类型语言转换为强制类型检验语言)配置项时,以上代码会提示以下错误信息:

Parameter 'x' implicitly has an 'any' type.
Parameter 'y' implicitly has an 'any' type.

TypeScript重构axios typescript 函数重载_函数重载


该信息告诉我们参数 x 和参数 y 隐式具有 any 类型。为了解决这个问题,我们可以为参数设置一个类型。因为我们希望 add 函数同时支持 string 和 number 类型,因此我们可以定义一个 string | number 联合类型

function add (x: number | string, y: number | string) {
    if (typeof x === 'string' || typeof y === 'string') {
        return x.toString() + y.toString();
    }
    return x + y;
}

设置了联合类型后,之前错误提示信息就消失了,接着下一步:

let result = add('hearts', 'spades');
result.split(' ');

我们想当然的以为result类型为string,然后就可以正常使用字符串对象上的split方法, 结果又出现了新问题, 提示number类型的对象上并不存在split属性。

Property 'split' does not exist on type 'string | number'.  
Property 'split' does not exist on type 'number'.

那怎么处理呢?
这时可以使用函数重载。可以为同一个函数提供多个函数类型定义来进行函数重载(函数名称相同,参数数量或类型不同, 或者参数数量相同同时参数顺序不同)。

如何进行定义?
定义函数重载需要定义重载签名和一个实现签名。
其中重载签名定义函数的形参和返回类型,没有函数体。
一个函数可以有多个重载签名:主要是可以精确显示函数的输入输出,对应于调用该函数的不同方式。
此外, 实现签名还具有参数类型和返回类型,而且还有实现函数的主体,且只能有一个实现签名。
实现签名主要是将所有的输入输出类型做一个全量定义,防止TS编译报错,实现签名就是整个整个函数实现的全部逻辑 。
如:

type Types = number | string;
// 重载签名
function add(x: number, y: number): number;
function add(x: string, y: string): string;
function add(x: string, y: number): string;
function add(x: number, y: string): string;
function add(x: Types, y: Types) {
    if (typeof x === 'string' || typeof y === 'string') {
        return x.toString() + y.toString();
    }
    return x + y;
}
const result = add('hearts', ' spades');
result.split(' ');

编译成功,编译结果如下:

TypeScript重构axios typescript 函数重载_TypeScript重构axios_02

以上定义了4个重载签名和一个实现签名。
需要注意的是: 当 TypeScript 编译器处理函数重载时,它会查找重载列表,尝试使用第一个重载定义。 如果匹配的话就使用这个。 因此,在定义重载的时候,一定要把最精确的定义放在最前面。

其他:
从编译后结果可以看出JavaScript 作为动态语言, 是没有函数重载这一说法的。原因如下:

  • 参数没有类型的区分;
  • 对参数个数也没有检查

既然语言层面无法自动进行重载,但借助其动态的特性,我们可以在代码中手动检查入参的类型,或者通过 arguments 获取到参数个数,从而实现根据不同的入参做不同的操作。

参考资料

https://www.tslang.cn/docs/handbook/functions.html

https://cloud.tencent.com/developer/article/1599888

https://cloud.tencent.com/developer/article/2003156