

这是 AWS CDK v2 开发者指南。旧版 CDK v1 于 2022 年 6 月 1 日进入维护阶段，并于 2023 年 6 月 1 日终止支持。

本文属于机器翻译版本。若本译文内容与英语原文存在差异，则一律以英文原文为准。

# 在 TypeScript 中使用 AWS CDK
<a name="work-with-cdk-typescript"></a>

TypeScript 是一种 AWS 云开发工具包 (AWS CDK) 完全支持且视为稳定的客户端语言。在 TypeScript 中使用 AWS CDK 采用了熟悉的工具，包括 Microsoft 的 TypeScript 编译器 (`tsc`)、[Node.js](https://nodejs.org/) 和节点包管理器 (`npm`)。如果您愿意，也可以使用 [Yarn](https://yarnpkg.com/)，不过本指南中的示例使用的是 NPM。通过 NPM 存储库 [npmjs.org](https://www.npmjs.com/) 来分发构成 AWS 构造库的模块。

您可以使用任何编辑器或 IDE。许多 AWS CDK 开发人员都使用 [Visual Studio Code](https://code.visualstudio.com/)（或其开源等效版本 [VSCodium](https://vscodium.com/)），该编辑器可为 TypeScript 提供卓越支持。

## 开始使用 TypeScript
<a name="typescript-prerequisites"></a>

要使用 AWS CDK，您必须拥有 AWS 账户和凭证，并已安装 Node.js 和 AWS CDK 工具包。请参阅[开始使用 AWS CDK](getting-started.md)。

您还需要 TypeScript 本身（版本 3.8 或更高版本）。如果您还没有 TypeScript，可使用 `npm` 进行安装。

```
$ npm install -g typescript
```

**注意**  
如果遇到权限错误，且在系统上拥有管理员访问权限，请尝试 `sudo npm install -g typescript`。

通过定期的 `npm update -g typescript` 让 TypeScript 保持为最新版本。

**注意**  
第三方语言弃用：语言版本仅在供应商或社区共享其 EOL（生命周期终止）之前才受支持，如有更改，会另行通知。

## 创建项目
<a name="typescript-newproject"></a>

您可以通过在空目录中调用 `cdk init` 来创建一个新的 AWS CDK 项目。使用 `--language` 选项并指定 `typescript`：

```
$ mkdir my-project
$ cd my-project
$ cdk init app --language typescript
```

创建项目还会安装 [https://docs.aws.amazon.com/cdk/api/v2/docs/aws-cdk-lib-readme.html](https://docs.aws.amazon.com/cdk/api/v2/docs/aws-cdk-lib-readme.html) 模块及其依赖项。

 `cdk init` 使用项目文件夹的名称来命名项目的各种元素，包括类、子文件夹和文件。文件夹名称中的连字符都将转换为下划线。但是，名称应遵循 TypeScript 标识符的形式；例如，名称不应以数字开头，也不应包含空格。

## 使用本地 `tsc` 和 `cdk`
<a name="typescript-local"></a>

在大多数情况下，本指南假设您已全局安装 TypeScript 和 CDK Toolkit (`npm install -g typescript aws-cdk`)，并且提供的命令示例（例如 `cdk synth`）也遵循此假设。这种方法可以很容易让两个组件保持为最新版本，并且由于两者都采取了严格的向后兼容性方法，所以始终使用最新版本通常风险很小。

有些团队更喜欢在每个项目中指定所有依赖项，包括 TypeScript 编译器和 CDK Toolkit 等工具。通过这种做法，您可以将这些组件固定到特定版本，并确保团队中的所有开发人员（以及您的 CI/CD 环境）都正好使用这些版本。这从根源上消除了可能发生的变更，有助于使构建和部署更加一致且可重复。

CDK 在 TypeScript 项目模板的 `package.json` 中包含了 TypeScript 和 CDK 工具包的依赖项，因此，如果您想使用这种方法，无需对项目进行任何更改。您所需要做的就是使用稍微不同的命令来构建应用程序以及发出 `cdk` 命令。


| 操作 | 使用全局工具 | 使用本地工具 | 
| --- | --- | --- | 
|   **初始化项目**   |   `cdk init --language typescript`   |   `npx aws-cdk init --language typescript`   | 
|   **构建**   |   `tsc`   |   `npm run build`   | 
|   **运行 CDK Toolkit 命令**   |   `cdk …​`   |   `npm run cdk …​` 或者`npx aws-cdk …​`   | 

 `npx aws-cdk` 运行当前项目中本地安装的 CDK Toolkit 版本（如果存在），如有则回退到全局安装。如果不存在全局安装，则 `npx` 会下载 CDK Toolkit 的临时副本并运行该副本。您可以使用 `@` 语法（`npx aws-cdk@2.0 --version` 打印 `2.0.0`）来指定 CDK Toolkit 的任意版本。

**提示**  
设置别名，以便您可以在安装本地 CDK Toolkit 时使用 `cdk` 命令。  

```
$ alias cdk="npx aws-cdk"
```

```
doskey cdk=npx aws-cdk $*
```

## 管理 AWS 构造库模块
<a name="typescript-managemodules"></a>

使用节点软件包管理器 (`npm`) 来安装和更新供您应用程序使用的 AWS 构造库模块以及您需要的其他软件包。（如果您愿意，可以用 `yarn` 代替 `npm`。）`npm` 还会自动安装这些模块的依赖项。

大多数 AWS CDK 构造都位于名为 `aws-cdk-lib` 的 CDK 主包中，这是 `cdk init` 创建的新项目中的默认依赖项。“实验性”AWS 构造库模块（其中更高级别的构造仍在开发中）的命名类似于 `@aws-cdk/<SERVICE-NAME>-alpha`。服务名称带有 *aws-* 前缀。如果您不确定某个模块的名称，请[在 NPM 上进行搜索](https://www.npmjs.com/search?q=%40aws-cdk)。

**注意**  
[CDK API Reference](https://docs.aws.amazon.com/cdk/api/v2/docs/aws-construct-library.html) 也显示了包名称。

例如，以下命令用于安装 AWS CodeStar 的实验模块。

```
$ npm install @aws-cdk/aws-codestar-alpha
```

某些服务的构造库支持位于多个命名空间中。例如，除 `aws-route53` 之外，还有另外三个 Amazon Route 53 命名空间，分别是 `aws-route53-targets`、`aws-route53-patterns` 和 `aws-route53resolver`。

您项目的依赖项在 `package.json` 中进行维护。您可以编辑此文件，将部分或全部依赖项锁定到特定版本，或者允许在特定条件下将其更新到较新版本。根据您在 `package.json` 中指定的规则，将项目的 NPM 依赖项更新到允许的最新版本：

```
$ npm update
```

在 TypeScript 中，您可以将模块导入到代码中，其名称与使用 NPM 安装模块时使用的名称相同。在应用程序中导入 AWS CDK 类和 AWS 构造库模块时，我们建议采用以下做法。遵循这些准则将有助于让您的代码与其他 AWS CDK 应用程序保持一致并更易于理解。
+ 请使用 ES6 风格的 `import` 指令，而非 `require()`。
+ 通常，从 `aws-cdk-lib` 中导入单个类。

  ```
  import { App, Stack } from 'aws-cdk-lib';
  ```
+ 如果您需要 `aws-cdk-lib` 中的许多类，则可以使用 `cdk` 的命名空间别名，而不是导入单个类。避免同时执行这两项操作。

  ```
  import * as cdk from 'aws-cdk-lib';
  ```
+ 通常，使用短命名空间别名来导入 AWS 服务构造。

  ```
  import { aws_s3 as s3 } from 'aws-cdk-lib';
  ```

## 在 TypeScript 中管理依赖项
<a name="work-with-cdk-typescript-dependencies"></a>

在 TypeScript CDK 项目中，在项目主目录的 `package.json` 文件中指定依赖项。核心 AWS CDK 模块位于名为 `aws-cdk-lib` 的单个 `NPM` 程序包中。

当您使用 `npm install` 来安装包时，NPM 会在 `package.json` 中记录该包。

如果您愿意，可以用 Yarn 代替 NPM。但是，CDK 不支持 Yarn 的即插即用模式，该模式是 Yarn 2 中的默认模式。将以下内容添加到项目的 `.yarnrc.yml` 文件以关闭此功能。

```
nodeLinker: node-modules
```

### CDK 应用程序
<a name="work-with-cdk-typescript-dependencies-apps"></a>

以下是 `cdk init --language typescript` 命令生成的示例 `package.json` 文件：

```
{
  "name": "my-package",
  "version": "0.1.0",
  "bin": {
    "my-package": "bin/my-package.js"
  },
  "scripts": {
    "build": "tsc",
    "watch": "tsc -w",
    "test": "jest",
    "cdk": "cdk"
  },
  "devDependencies": {
    "@types/jest": "^26.0.10",
    "@types/node": "10.17.27",
    "jest": "^26.4.2",
    "ts-jest": "^26.2.0",
    "aws-cdk": "2.16.0",
    "ts-node": "^9.0.0",
    "typescript": "~3.9.7"
  },
  "dependencies": {
    "aws-cdk-lib": "2.16.0",
    "constructs": "^10.0.0",
    "source-map-support": "^0.5.16"
  }
}
```

对于可部署的 CDK 应用程序，必须在 `package.json` 的 `dependencies` 部分中指定 `aws-cdk-lib`。您可以使用脱字符（^）版本号说明符来表示您将接受比指定版本更高的版本，前提是它们位于同一个主版本内。

对于实验性构造，请为 alpha 构造库模块指定确切版本，这些模块的 API 可能会发生更改。请勿使用 ^ 或 \$1，因为这些模块的更高版本可能会导致 API 发生更改，从而导致您的应用程序中断。

在 `package.json` 的 `devDependencies` 部分中指定测试应用程序所需的库和工具版本（例如，`jest` 测试框架）。或者使用 ^ 来指定可接受更高版本的兼容版本。

### 第三方构造库
<a name="work-with-cdk-typescript-dependencies-libraries"></a>

如果您正在开发构造库，请结合 `peerDependencies` 和 `devDependencies` 部分来指定其依赖项，如以下示例 `package.json` 文件所示。

```
{
  "name": "my-package",
  "version": "0.0.1",
  "peerDependencies": {
    "aws-cdk-lib": "^2.14.0",
    "@aws-cdk/aws-appsync-alpha": "2.10.0-alpha",
    "constructs": "^10.0.0"
  },
  "devDependencies": {
    "aws-cdk-lib": "2.14.0",
    "@aws-cdk/aws-appsync-alpha": "2.10.0-alpha",
    "constructs": "10.0.0",
    "jsii": "^1.50.0",
    "aws-cdk": "^2.14.0"
  }
}
```

在 `peerDependencies` 中，使用脱字符（^）来指定您的库所使用的 `aws-cdk-lib` 的最低版本。这样可以最大限度地提高您的库与一系列 CDK 版本的兼容性。为 alpha 构造库模块指定确切版本，这些模块的 API 可能会发生更改。使用 `peerDependencies` 可以确保 `node_modules` 树中所有 CDK 库副本只有一个副本。

在 `devDependencies` 中，指定测试所需的工具和库，也可以使用 ^ 来表示可以接受更高版本的兼容版本。确切指定（不带 ^ 或 \$1）您宣称您的库与之兼容的 `aws-cdk-lib` 和其他 CDK 软件包的最低版本。这种做法可确保您的测试是针对这些版本运行的。这样，如果您无意中使用了仅较新版本中具备的功能，则您的测试可以将其捕捉到。

**警告**  
 只有 NPM 7 及更高版本才能自动安装 `peerDependencies`。如果您使用的是 NPM 6 或更早版本，或者使用的是 Yarn，则必须在 `devDependencies` 中包含依赖项的依赖项。否则，将不会进行安装，并且您将收到有关对等依赖项未解决的警告。

### 安装和更新依赖项
<a name="work-with-cdk-typescript-dependencies-install"></a>

运行以下命令来安装项目的依赖项。

**Example**  

```
# Install the latest version of everything that matches the ranges in 'package.json'
npm install

# Install the same exact dependency versions as recorded in 'package-lock.json'
npm ci
```

```
# Install the latest version of everything that matches the ranges in 'package.json'
$ yarn upgrade

# Install the same exact dependency versions as recorded in 'yarn.lock'
$ yarn install --frozen-lockfile
```

要更新已安装的模块，可以使用前面的 `npm install` 和 `yarn upgrade` 命令。任一命令都会将 `node_modules` 中的软件包更新为可满足 `package.json` 中规则的最新版本。但是，它们不会更新 `package.json` 本身，您可能需要执行此操作来设置新的最低版本。如果您将包托管在 GitHub 上，则可以配置 [Dependabot 版本更新](https://docs.github.com/en/code-security/dependabot/dependabot-version-updates/configuring-dependabot-version-updates) 来自动更新 `package.json`。或者，可以使用 [npm-check-updates](https://www.npmjs.com/package/npm-check-updates)。

**重要**  
根据设计，当您安装或更新依赖项时，NPM 和 Yarn 会选择可满足 `package.json` 中指定要求的每个包的最新版本。这些版本始终存在损坏风险（无论是无意还是有意）。更新项目的依赖项后，请彻底进行测试。

## TypeScript 中的 AWS CDK 习语
<a name="typescript-cdk-idioms"></a>

### Props
<a name="typescript-props"></a>

所有 AWS 构造库类都使用三个参数进行实例化：在其中定义构造的 *scope*（构造树中的父级）、*id* 和 *props*。*props* 参数是键/值对捆绑包，构造使用这些键/值对来配置其创建的 AWS 资源。其他类和方法也使用“属性捆绑包”模式作为参数。

在 TypeScript 中，`props` 的形状是使用一个接口定义的，该接口可显示必需参数和可选参数及其类型。为每种 `props` 参数定义一个此类接口，通常特定于单个构造或方法。例如，[https://docs.aws.amazon.com/cdk/api/v2/docs/aws-cdk-lib.aws_s3.Bucket.html](https://docs.aws.amazon.com/cdk/api/v2/docs/aws-cdk-lib.aws_s3.Bucket.html) 构造（在 `aws-cdk-lib/aws-s3` 模块中）指定符合 [https://docs.aws.amazon.com/cdk/api/v2/docs/aws-cdk-lib.aws_s3.BucketProps.html](https://docs.aws.amazon.com/cdk/api/v2/docs/aws-cdk-lib.aws_s3.BucketProps.html) 接口的 `props` 参数。

如果属性本身就是一个对象，例如 `BucketProps` 的 [https://docs.aws.amazon.com/cdk/api/v2/docs/aws-cdk-lib.aws_s3.BucketProps.html#websiteredirect](https://docs.aws.amazon.com/cdk/api/v2/docs/aws-cdk-lib.aws_s3.BucketProps.html#websiteredirect) 属性，则该对象将拥有自己的接口，其形状必须符合该接口，在本例中为 [https://docs.aws.amazon.com/cdk/api/v2/docs/aws-cdk-lib.aws_s3.RedirectTarget.html](https://docs.aws.amazon.com/cdk/api/v2/docs/aws-cdk-lib.aws_s3.RedirectTarget.html)。

如果正在子类化一个 AWS 构造库类（或重写采用类似 props 参数的方法），则可以从现有接口继承来创建一个新接口，该新接口可指定您的代码所需的任何新 props。在调用父类或基方法时，通常可以传递收到的整个 props 参数，因为对象中提供但未在接口中指定的所有属性都将被忽略。

AWS CDK 的未来版本可能会碰巧添加一个新属性，其名称与您用于自己属性的名称相同。将收到的值沿继承链向上传递可能会导致意外行为。更安全的做法是传递您收到的 props 卷影副本，同时移除属性或将其设置为 `undefined`。例如：

```
super(scope, name, {...props, encryptionKeys: undefined});
```

或者，为您的属性命名，以便清楚地表明它们属于您的构造。这样，其就不太可能在未来的 AWS CDK 版本中与属性发生冲突。如果有很多属性，请使用一个适当命名的对象来存放它们。

### 缺失值
<a name="typescript-missing-values"></a>

对象中的缺失值（例如 props）在 TypeScript 中的值为 `undefined`。该语言的 3.7 版本引入了简化处理这些值的运算符，这样在达到未定义的值时就能更容易指定默认值和“短路”链接。有关这些功能的更多信息，请参阅 [TypeScript 3.7 发行说明](https://www.typescriptlang.org/docs/handbook/release-notes/typescript-3-7.html)，特别是前两个功能“可选链接”和“Null 值合并”。

## 构建和运行 CDK 应用程序
<a name="typescript-running"></a>

通常，构建和运行应用程序时，您应位于项目的根目录中。

Node.js 无法直接运行 TypeScript；相反，使用 TypeScript 编译器 `tsc` 将您的应用程序转换到 JavaScript。然后执行生成的 JavaScript 代码。

每当需要运行您的应用程序时，AWS CDK 都会自动执行此操作。但是，手动编译以检查错误并运行测试可能会很有用。要手动编译您的 TypeScript 应用程序，请发出 `npm run build` 命令。您也可以发出 `npm run watch` 命令进入监视模式，在这种模式下，每当您保存对源文件的更改时，TypeScript 编译器都会自动重建您的应用程序。