一、函数类型

下面两个函数声明了参数的类型以及函数返回值的类型,上面是具名函数,下面是匿名函数

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

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

虽然上面已经定义了函数的类型,但是匿名函数并没有定义完整的函数类型

完整的函数类型

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

完整的函数类型包括:参数类型返回值类型

首先声明了参数类型 :(x: number, y: number) ,然后声明了返回值类型 => number =

而参数的名字实际上可以不一致,上面示例中虽然写了相同的名字,但是可以写成两个不同的参数名称:

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

1.png

返回值类型通过 => 进行表示,如果一个函数返回值是 void ,也必须声明是 void,不能省略因为完整的函数类型包含参数类型和返回值类型两个部分,一个也不能省略。

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

2.png

函数的类型只是由参数类型和返回值组成的。

函数中使用的捕获变量不会体现在类型里。 实际上,这些变量是函数的隐藏状态并不是组成API的一部分。

所谓的捕获变量,与函数本身是无关的,因为这个是由函数作用域外(父级作用域或更高级作用域)声明的变量,只是在函数中使用了而已

const a:number = 1;
let add:(x: number) => number = function(x: number) :number {
    return a + x;
}

推断类型

完整的函数类型中,需要我们明确标示参数的类型是什么,但是如果两边的参数中只有一边的参数声明了类型,typescript 会尝试去识别类型:

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

上面代码中,因为 baseValueincrement 已经声明了类型是 number,所以 x 和 y 会推断出类型是 number。

3.png

二、可选参数和默认参数

TypeScript里的每个函数参数都是必须的。

这不是指不能传递 nullundefined 作为参数,而是说编译器检查用户是否为每个参数都传入了值。

编译器还会假设只有这些参数会被传递进函数。

简短地说,传递给一个函数的参数个数必须与函数期望的参数个数一致

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

add(1,2);
add(1); // 错误
add(1,2,3); // 错误

上面 add 方法的使用中,后面两种都是错误的,因为传入的参数个数与期望的个数不相符合

4.png

如果需要声明某个参数是可选参数,则可以通过 ? 来标示:

function add(x:number, y:number, z?: number): number {
    z = z || 0;
    return x + y + z;
}

上面代码中通过 z? 声明 z 可选参数,因此传入 2 个或者3个参数都是可以的。

5.png

然而上面代码为了兼容 z 没有值得时候,写了个默认值处理 z = z || 0,而 ES6 有了函数参数默认值特性之后,这种写法基本都是被废弃的,在 typescript 中,可以使用同样的方式声明函数参数的默认值:

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

add(1, 3);
add(1,2,3);
add(1,2,3,4)

而一旦声明了默认值之后,就表示这个值可有可无,类似于已经加了 z? 声明。

三、剩余参数

ES6 同样拥有 rest 参数,也就是剩余参数。它可以表示任意剩下的参数,本质是一个数组。

typescript 中同样通过省略号来表示剩下的多个参数:

function add(x: number, ...otherNum:number[]): number {
   otherNum && otherNum.forEach((item) => {
    x += item;
  });
   return x;
}
add(1, 3, 4, 54, 5, 6);

四、this

JavaScript里,this 的值在函数被调用的时候才会指定。

但是需要花点时间弄清楚函数调用的上下文是什么

let deck = {
    suits: ["hearts", "spades", "clubs", "diamonds"],
    cards: Array(52),
    createCardPicker: function() {
        return function() {
            let pickedCard = Math.floor(Math.random() * 52);
            let pickedSuit = Math.floor(pickedCard / 13);

            return {suit: this.suits[pickedSuit], card: pickedCard % 13};
        }
    }
}

let cardPicker = deck.createCardPicker();
let pickedCard = cardPicker();

alert("card: " + pickedCard.card + " of " + pickedCard.suit);

可以看到 createCardPicker 是个函数,并且它又返回了一个函数。

如果我们尝试运行这个程序,会发现它并没有弹出对话框而是报错了。

因为 createCardPicker 返回的函数里的 this 被设置成了 window 而不是 deck 对象。 因为我们只是独立的调用了 `cardPicker()。 顶级的非方法式调用会将 this 视为 window。 (注意:在严格模式下, this 为 undefined 而不是 window)。

为了解决这个问题,我们可以在函数被返回时就绑好正确的 this。

这样的话,无论之后怎么使用它,都会引用绑定的 deck 对象。

我们需要改变函数表达式来使用 ECMAScript 6 箭头语法。 箭头函数能保存函数创建时的 this值,而不是调用时的值:

let deck = {
    suits: ["hearts", "spades", "clubs", "diamonds"],
    cards: Array(52),
    createCardPicker: function() {
        // NOTE: the line below is now an arrow function, allowing us to capture 'this' right here
        return () => {
            let pickedCard = Math.floor(Math.random() * 52);
            let pickedSuit = Math.floor(pickedCard / 13);

            return {suit: this.suits[pickedSuit], card: pickedCard % 13};
        }
    }
}

let cardPicker = deck.createCardPicker();
let pickedCard = cardPicker();

alert("card: " + pickedCard.card + " of " + pickedCard.suit);

更好事情是,TypeScript 会警告你犯了一个错误,如果你给编译器设置了 --noImplicitThis 标记。 它会指出 this.suits[pickedSuit] 里的 this 的类型为 any。

6.png

this 参数

不幸的是,this.suits[pickedSuit] 的类型依旧为 any。 这是因为 this 来自对象字面量里的函数表达式。

修改的方法是,提供一个显式的 this 参数。 this 参数是个假的参数,它出现在参数列表的最前面:

function f(this: void) {
    // make sure `this` is unusable in this standalone function
}

五、重载

JavaScript里函数根据传入不同的参数而返回不同类型的数据非常常见

至于 JavaScript 的函数重载不过多阐述,可以看下高程这部分的介绍。

举个例子:

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

add(1, 2);
add(1, '22');

上面代码不会报错,因为 function add(x: number, y: number): number;function add(x: number, y: string): string; 已经声明了函数 add 可能有两种参数类型或者返回值类型,但是需要注意的是这两个只是生命,并没有函数体。

function add(x: any, y: any): any {
    if (typeof y === 'string') { 
        return '' + x + y;
    }
    return x + y;
}

上面其实并不是重载,因为只有两个重载,一个接受 number 一个接受 string,而 any 则是对其进行处理而已。

为了让编译器能够选择正确的检查类型,它与JavaScript里的处理流程相似。 它查找重载列表,尝试使用第一个重载定义。 如果匹配的话就使用这个。 因此,在定义重载的时候,一定要把最精确的定义放在最前面。