函数是JavaScript应用程序的基础。 它帮助你实现抽象层,模拟类,信息隐藏和模块。 在TypeScript里,虽然已经支持类,命名空间和模块,但函数仍然是主要的定义行为的地方。 TypeScript为JavaScript函数添加了额外的功能,让我们可以更容易地使用。

函数类型定义

我们可以给每个参数添加类型之后再为函数本身添加返回值类型。 TypeScript能够根据返回语句自动推断出返回值类型,因此我们通常可以省略它。

  1. function add(x: number, y: number): number {
  2. return x + y;
  3. }

完整的函数类型

  1. const myadd: (x: number, y: number) => number = (x: number, y: number): number => {
  2. return x + y;
  3. };

函数类型包含两部分:参数类型和返回值类型。 当写出完整函数类型的时候,这两部分都是需要的。 我们以参数列表的形式写出参数类型,为每个参数指定一个名字和类型。

这个参数的名字只是为了增加可读性。 我们也可以这么写:

  1. const myadd: (firt: number, second: number) => number = (x: number, y: number): number => {
  2. return x + y;
  3. };

只要参数类型是匹配的,那么就认为它是有效的函数类型,而不在乎参数名是否正确。 第二部分是返回值类型。 对于返回值,我们在函数和返回值类型之前使用(=>)符号,使之清晰明了。 如之前提到的,返回值类型是函数类型的必要部分,如果函数没有返回任何值,你也必须指定返回值类型为void而不能留空。 函数的类型只是由参数类型和返回值组成的。 函数中使用的捕获变量不会体现在类型里。 实际上,这些变量是函数的隐藏状态并不是组成API的一部分。

类型推导

  1. const myAdd: (baseValue: number, increment: number) => number =
  2. (x, y) => x + y;

我们可以看到,在此处,等式仅有一侧带上了类型,但是typescript编译器仍然可以正确识别。
这叫做“按上下文归类”,是类型推论的一种。 它帮助我们更好地为程序指定类型。

参数

TypeScript里的每个函数参数都是必须的。 这不是指不能传递nullundefined作为参数,而是说编译器检查用户是否为每个参数都传入了值。 编译器还会假设只有这些参数会被传递进函数。传递给一个函数的参数个数必须与函数期望的参数个数一致。

  1. function buildName(firstName: string, lastName: string): string {
  2. return firstName + " " + lastName;
  3. }
  4. // let result1 = buildName("Bob"); // error, too few parameters
  5. // let result2 = buildName("Bob", "Adams", "Sr."); // error, too many parameters
  6. const result3 = buildName("Bob", "Adams"); // ah, just right


可选参数

JavaScript里,每个参数都是可选的,可传可不传。 没传参的时候,它的值就是undefined。 在TypeScript里我们可以在参数名旁使用?实现可选参数的功能。 比如,我们想让last name是可选的:

  1. function buildName(firstName: string, lastName?: string): string {
  2. if (lastName) {
  3. return firstName + " " + lastName;
  4. } else {
  5. return firstName;
  6. }
  7. }
  8. const result1 = buildName("Bob"); // works correctly now
  9. // let result2 = buildName("Bob", "Adams", "Sr."); // error, too many parameters
  10. const result3 = buildName("Bob", "Adams"); // ah, just right

可选参数必须跟在必须参数后面。 如果上例我们想让first name是可选的,那么就必须调整它们的位置,把first name放在后面。


在TypeScript里,我们也可以为参数提供一个默认值当用户没有传递这个参数或传递的值是undefined时。 它们叫做有默认初始化值的参数。 让我们修改上例,把last name的默认值设置为"Smith"

  1. function buildName(firstName: string, lastName: string = "Smith"): string {
  2. return firstName + " " + lastName;
  3. }
  4. const result1 = buildName("Bob"); // works correctly now, returns "Bob Smith"
  5. const result2 = buildName("Bob", undefined); // still works, also returns "Bob Smith"
  6. // let result3 = buildName("Bob", "Adams", "Sr."); // error, too many parameters
  7. const result4 = buildName("Bob", "Adams"); // ah, just right

剩余参数

要了解这部分,需要先行了解es6的rest参数

  1. function buildName(firstName: string, ...restOfName: string[]): string {
  2. return firstName + " " + restOfName.join(" ");
  3. }
  4. const employeeName = buildName("Joseph", "Samuel", "Lucas", "MacKinzie");

我们也可以这么写:

  1. function buildName(firstName: string, ...restOfName: string[]) {
  2. return firstName + " " + restOfName.join(" ");
  3. }
  4. let buildNameFun: (fname: string, ...rest: string[]) => string = buildName;

关于this

推荐使用箭头函数,或者在函数定义的时候进行绑定。

重载

  1. function reverse(x: number): number;
  2. function reverse(x: string): string;
  3. function reverse(x: number | string): number | string {
  4. if (typeof x === "number") {
  5. return Number(x.toString().split("").reverse().join(""));
  6. } else if (typeof x === "string") {
  7. return x.split("").reverse().join("");
  8. }
  9. }

上例中,我们重复定义了多次函数 reverse,前几次都是函数定义,最后一次是函数实现。在编辑器的代码提示中,可以正确的看到前两个提示。 注意,TypeScript 会优先从最前面的函数定义开始匹配,所以多个函数定义如果有包含关系,需要优先把精确的定义写在前面。

附上官方的例子:

  1. const suits = ["hearts", "spades", "clubs", "diamonds"];
  2. function pickCard(x: Array<{suit: string; card: number; }>): number;
  3. function pickCard(x: number): {suit: string; card: number; };
  4. function pickCard(x: string | number | any[]): any {
  5. // Check to see if we're working with an object/array
  6. // if so, they gave us the deck and we'll pick the card
  7. if (typeof x === "object") {
  8. const pickedCard = Math.floor(Math.random() * x.length);
  9. return pickedCard;
  10. } else if (typeof x === "number") {
  11. const pickedSuit = Math.floor(x / 13);
  12. return { suit: suits[pickedSuit], card: x % 13 };
  13. }
  14. }
  15. const myDeck = [{ suit: "diamonds", card: 2 }, { suit: "spades", card: 10 }, { suit: "hearts", card: 4 }];
  16. const pickedCard1 = myDeck[pickCard(myDeck)];
  17. alert("card: " + pickedCard1.card + " of " + pickedCard1.suit);
  18. const pickedCard2 = pickCard(15);
  19. alert("card: " + pickedCard2.card + " of " + pickedCard2.suit);