函数是JavaScript中任何应用的基本构件。它们是你如何建立抽象层,模仿类、信息隐藏和模块。在TypeScript中,虽然有类、命名空间和模块,但函数仍然在描述如何做事情方面扮演着关键的角色。TypeScript还为标准的JavaScript函数增加了一些新的功能,使它们更容易使用。

函数

首先,就像在JavaScript中一样,TypeScript函数可以被创建为一个命名函数或匿名函数。这允许你为你的应用程序选择最合适的方法,无论你是在一个API中建立一个函数列表,还是一个一次性函数交给另一个函数。

快速回顾一下这两种方法在JavaScript中是什么样子的。

  1. // Named function
  2. function add(x, y) {
  3. return x + y;
  4. }
  5. // Anonymous function
  6. let myAdd = function (x, y) {
  7. return x + y;
  8. };

就像在JavaScript中一样,函数可以引用函数体之外的变量。当它们这样做时,它们被称为捕获这些变量。虽然理解这如何工作(以及使用这种技术时的权衡)超出了本文的范围,但牢固理解这种机制如何工作是使用JavaScript和TypeScript的一个重要部分。

  1. let z = 100;
  2. function addToZ(x, y) {
  3. return x + y + z;
  4. }

功能类型

输入函数

让我们在前面的简单例子中加入类型。

  1. function add(x: number, y: number): number {
  2. return x + y;
  3. }
  4. let myAdd = function (x: number, y: number): number {
  5. return x + y;
  6. };

我们可以给每个参数添加类型,然后给函数本身添加返回类型。TypeScript可以通过查看返回语句来计算出返回类型,所以在很多情况下,我们也可以选择不写这个。

编写函数类型

现在我们已经将函数类型化了,让我们通过观察函数类型的每一块,将函数的完整类型写出来。

  1. let myAdd: (x: number, y: number) => number = function (
  2. x: number,
  3. y: number
  4. ): number {
  5. return x + y;
  6. };

一个函数的类型同样有两部分:参数的类型和返回类型。在写出整个函数类型的时候,这两部分都是必须的。我们在写出参数类型的时候,就像写参数列表一样,给每个参数起一个名字和一个类型。这个名字只是为了帮助阅读性。我们可以改写成

  1. let myAdd: (baseValue: number, increment: number) => number = function (
  2. x: number,
  3. y: number
  4. ): number {
  5. return x + y;
  6. };

只要参数类型一致,就认为是函数的有效类型,不管你在函数类型中给参数起什么名字。

第二部分是返回类型。我们通过在参数和返回类型之间使用一个箭头(=>)来明确哪个是返回类型。如前所述,这是函数类型中的必填部分,所以如果函数没有返回值,你会使用void而不是不写。

值得注意的是,只有参数和返回类型构成了函数类型。捕获的变量不会反映在类型中。实际上,捕获的变量是任何函数的 “隐藏状态 “的一部分,并不构成其API。

推断类型

在玩这个例子的时候,你可能会注意到TypeScript编译器可以计算出类型,即使你在等式的一边只有类型。

  1. // The parameters 'x' and 'y' have the type number
  2. let myAdd = function (x: number, y: number): number {
  3. return x + y;
  4. };
  5. // myAdd has the full function type
  6. let myAdd2: (baseValue: number, increment: number) => number = function (x, y) {
  7. return x + y;
  8. };

这就是所谓的 “上下文类型”,一种类型推理的形式。这有助于减少你的程序的类型化工作量。

可选和默认参数
在TypeScript中,每个参数都被假定为函数所需。这并不意味着它不能被赋予null或未定义,而是当函数被调用时,编译器会检查用户是否为每个参数提供了一个值。编译器还假设这些参数是唯一会传递给函数的参数。简而言之,给函数的参数数必须与函数期望的参数数相匹配。

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

在JavaScript中,每一个参数都是可选的,用户可以根据自己的需要将它们去掉。当他们这样做时,他们的值是未定义的。在TypeScript中,我们可以通过在我们希望成为可选项的参数末尾添加一个?来获得这个功能。例如,让我们说我们希望上面的姓氏参数是可选的。

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

任何可选参数必须跟在所需参数后面。如果我们想让第一个名字成为可选的,而不是最后一个名字,我们需要改变函数中参数的顺序,将第一个名字放在列表的最后。

在TypeScript中,我们也可以设置一个参数的值,如果用户没有提供一个参数,或者如果用户在它的位置上传递了undefined,那么这个值将被分配。这些被称为默认-初始化参数。让我们以前面的例子为例,将姓氏默认为 “Smith”。

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

在所有必要参数之后的默认初始化参数被视为可选参数,就像可选参数一样,在调用各自的函数时可以省略。这意味着可选参数和后面的缺省参数在类型上会有共性,所以这两个

  1. function buildName(firstName: string, lastName?: string) {
  2. // ...
  3. }

  1. function buildName(firstName: string, lastName = "Smith") {
  2. // ...
  3. }

共享同一类型(firstName: string, lastName?: string) => string。lastName的默认值在类型中消失,只留下参数是可选的事实。

与普通的可选参数不同,默认初始化参数不需要出现在所需参数之后。如果一个默认初始化参数出现在需要参数之前,用户需要显式传递undefined来获得默认初始化值。例如,我们可以写上一个例子,只在firstName上写一个默认初始化值。

  1. function buildName(firstName = "Will", lastName: string) {
  2. return firstName + " " + lastName;
  3. }
  4. let result1 = buildName("Bob"); // error, too few parameters
  5. Expected 2 arguments, but got 1.
  6. let result2 = buildName("Bob", "Adams", "Sr."); // error, too many parameters
  7. Expected 2 arguments, but got 3.
  8. let result3 = buildName("Bob", "Adams"); // okay and returns "Bob Adams"
  9. let result4 = buildName(undefined, "Adams"); // okay and returns "Will Adams"