函数是 JavaScript 的第一等公民,函数在 JavaScript 中可以实现抽象层、模拟类、信息隐藏和模块等等。TypeScript 在一定基础上扩展了函数的功能。
函数类型
函数返回值类型
我们可以给每个参数添加类型之后再为函数本身添加返回值类型。TypeScript 能够根据返回语句自动推断出返回值类型,所以通常可以省略它:
function add(x: number, y: number): number {
return x + y;
}
// 匿名函数
let myAdd = function(x: number, y: number): number {
return x + y;
};
完整函数类型
完整的函数类型包含两部分:参数类型和返回值类型。
// (x: number, y: number) => number 为函数的类型
// 其中的 x、y 只是为了增加可读性,只要参数类型是匹配的,无需匹配参数名是否一样
// 参数类型和返回值类型之间使用 => 符号
let myAdd: (x: number, y: number) => number = function(
x: number,
y: number
): number {
return x + y;
};
返回值类型是函数类型的必要部分,函数没有返回值的情况,也必须要指定返回值为 void
。
推断类型
函数定义的时候,如果赋值语句只有一边指定了语句,TypeScript 编译器会自动识别出类型:
// myAdd has the full function type
let myAdd = function(x: number, y: number): number {
return x + y;
};
// The parameters `x` and `y` have the type number
let myAdd: (baseValue: number, increment: number) => number = function(x, y) {
return x + y;
};
这叫做“按上下文归类”,是类型推论的一种。
可选参数和默认参数
默认情况下,TypeScript 会判断传入函数的参数和函数定义的参数是否一致,个数、类型都会进行判断。
let myAdd: (baseValue: number, increment: number) => number = function(x, y) {
return x + y;
};
// An argument for 'increment' was not provided.
myAdd(1);
myAdd(1, 2);
// 应有 2 个参数,但获得 3 个。
myAdd(1, 2, 3);
在 TypeScript 中,可以加上 ?
实现可选参数的功能,可选参数必须放在必需参数的前面:
function buildName(firstName: string, lastName?: string) {
if (lastName) {
return firstName + ' ' + lastName;
} else {
return firstName;
}
}
// Bob
let result1 = buildName('Bob');
// 应有 1-2 个参数,但获得 3 个。
let result2 = buildName('Bob', 'Adams', 'Sr.');
// Bob Adams
let result3 = buildName('Bob', 'Adams');
在定义函数的时候,可以给形参进行赋值作为默认值,仅当不传值或传入的值为 undefined
时,函数会使用这个默认值:
function buildName(firstName: string, lastName = 'Smith') {
return firstName + ' ' + lastName;
}
// Bob Smith
let result1 = buildName('Bob');
// Bob Smith
let result2 = buildName('Bob', undefined);
函数的默认值不需要放到必需参数的后面,但是,放在必需参数前面的默认值,只有显式地传递 undefined
时会生效。
剩余参数
在 JavaScript 中,可以使用 arguments
访问传入的所有参数;在 TypeScript 中同样可以使用。
剩余参数将不定数量的参数当作一个数组,表示 0 或多个参数:
// ... 后面加上一个数组,这就是剩余参数
function buildName(firstName: string, ...restOfName: string[]) {
return firstName + ' ' + restOfName.join(' ');
}
// Bob
console.log(buildName('Bob'));
// Bob Smith Steven
console.log(buildName('Bob', 'Smith', 'Steven'));
言下之意,剩余参数必须是最后的参数,不能够放到其他参数的前面。
重载
重载允许一个函数接受不同数量或类型的参数时,作出不同的处理。在 JavaScript 中,一般是判断传入参数的个数或类型等等情况实现重载。
以 TypeScript 中联合类型的特性,实现 JavaScript 版本的重载:
function reverse(x: number | string): number | string {
if (typeof x === 'number') {
return Number(
x
.toString()
.split('')
.reverse()
.join('')
);
}
return x
.split('')
.reverse()
.join('');
}
// function reverse(x: string | number): string | number
console.log(reverse(123));
console.log(reverse('123'));
上面是是实现重载的一个方法,但是这样实现重载更难懂,不能精确表达。TypeScript 提供更能精确表达的重载方法:
function reverse(x: number): number;
function reverse(x: string): string;
function reverse(x: number | string): number | string {
if (typeof x === 'number') {
return Number(
x
.toString()
.split('')
.reverse()
.join('')
);
}
return x
.split('')
.reverse()
.join('');
}
// function reverse(x: number): number (+1 overload)
console.log(reverse(123));
// function reverse(x: string): string (+1 overload)
console.log(reverse('123'));
注意,TypeScript 会优先从最前面的函数定义开始匹配,所以多个函数定义如果有包含关系,需要优先把精确的定义写在前面。