TypeScript 初识 - 函数

函数是 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 会优先从最前面的函数定义开始匹配,所以多个函数定义如果有包含关系,需要优先把精确的定义写在前面。

相关推荐