遵循 TypeScript 最佳实践 - AWS 规范性指导

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

遵循 TypeScript 最佳实践

TypeScript 是一种扩展功能的语言 JavaScript。它是一种强类型化且面向对象的语言。您可以使用 TypeScript 来指定在代码中传递的数据类型,并能够在类型不匹配时报告错误。本节概述了 TypeScript 最佳实践。

描述您的数据

您可以使用 TypeScript 来描述代码中对象和函数的形状。使用 any 类型相当于选择不对变量进行类型检查。建议避免在代码中使用 any。下面是一个例子。

type Result = "success" | "failure" function verifyResult(result: Result) { if (result === "success") { console.log("Passed"); } else { console.log("Failed") } }

使用枚举

您可以使用枚举定义一组命名常量以及可在代码库中重用的标准。我们建议您在全局级别导出一次枚举,然后让其他类导入并使用枚举。假设您想创建一组可能的操作来捕获代码库中的事件。 TypeScript 提供数字和基于字符串的枚举。以下示例使用了枚举。

enum EventType { Create, Delete, Update } class InfraEvent { constructor(event: EventType) { if (event === EventType.Create) { // Call for other function console.log(`Event Captured :${event}`); } } } let eventSource: EventType = EventType.Create; const eventExample = new InfraEvent(eventSource)

用户接口

接口是类的合同。如果您创建合同,则用户必须遵守合同。在以下示例中,使用接口来实现 props 标准化,确保调用方在使用该类时提供预期的参数。

import { Stack, App } from "aws-cdk-lib"; import { Construct } from "constructs"; interface BucketProps { name: string; region: string; encryption: boolean; } class S3Bucket extends Stack { constructor(scope: Construct, props: BucketProps) { super(scope); console.log(props.name); } } const app = App(); const myS3Bucket = new S3Bucket(app, { name: "my-bucket", region: "us-east-1", encryption: false })

某些属性只能在首次创建对象时修改。您可以在属性名称前加上 readonly 进行指定,如下面的示例所示。

interface Position { readonly latitude: number; readonly longitute: number; }

扩展接口

扩展接口可以减少重复,因为您不必在接口之间复制属性。此外,代码读取器也能轻松理解应用程序中的关系。

interface BaseInterface{ name: string; } interface EncryptedVolume extends BaseInterface{ keyName: string; } interface UnencryptedVolume extends BaseInterface { tags: string[]; }

避免使用空接口

我们建议您避免使用空接口,因为它们会带来潜在风险。在以下示例中,有一个名为的空接口BucketPropsmyS3Bucket1myS3Bucket2 对象都是有效的,但它们遵循不同的标准,因为接口不强制执行任何合同。以下代码将编译和打印属性,但这会在您的应用程序中产生不一致。

interface BucketProps {} class S3Bucket implements BucketProps { constructor(props: BucketProps){ console.log(props); } } const myS3Bucket1 = new S3Bucket({ name: "my-bucket", region: "us-east-1", encryption: false, }); const myS3Bucket2 = new S3Bucket({ name: "my-bucket", });

使用工厂

在抽象工厂模式中,接口负责创建相关对象的工厂,而无需明确指定其类。例如,您可以创建一个 Lambda 工厂来创建 Lambda 函数。您不是在构造中创建新的 Lambda 函数,而是将创建过程委托给工厂。有关此设计模式的更多信息,请参阅 Refactoring.G ur TypeScript u 文档中的抽象工厂

对属性使用解构

ECMAScript 6 (ES6) 中引入的解构 JavaScript 功能使您能够从数组或对象中提取多段数据,并将它们分配给它们自己的变量。

const object = { objname: "obj", scope: "this", }; const oName = object.objname; const oScop = object.scope; const { objname, scope } = object;

定义标准命名约定

强制执行命名约定可以保持代码库的一致性,并在考虑如何命名变量时减少开销。我们建议执行下列操作:

  • 对变量和函数名称使用 camelCase。

  • PascalCase 用于类名和接口名。

  • 对接口成员使用 camelCase。

  • PascalCase 用于类型名称和枚举名称。

  • 用 camelCase 命名文件(例如,ebsVolumes.tsxstorage.tsb

不要使用 var 关键字

let语句用于在中声明局部变量 TypeScript。它与关键字类似,但与var关键字相比,它在范围上var有一些限制。在 let 块中声明的变量只能在该块中使用。var关键字不能是块作用域的,这意味着它可以在特定块(由{})之外访问,但不能在定义它的函数之外访问。您可以重新声明和更新var变量。最佳做法是避免使用var关键字。

考虑使用 ESLint 和 Prettier

ESLint 可静态分析代码,快速发现问题。您可以使用 ESLint 创建一系列断言(称为 lint 规则),这些断言用来定义代码的外观或行为。ESLint 还提供了自动修复程序建议,可以帮助您改进代码。最后,您可以使用 ESLint 从共享插件加载 lint 规则。

Prettier 是一个知名的代码格式化程序,它支持各种不同的编程语言。您可以使用 Prettier 设置代码样式,避免手动格式化代码。安装后,您可以更新 package.json 文件并运行 npm run formatnpm run lint 命令。

以下示例向您展示了如何为项目启用 ESLint 和 Prettier 格式化程序。 AWS CDK

"scripts": { "build": "tsc", "watch": "tsc -w", "test": "jest", "cdk": "cdk", "lint": "eslint --ext .js,.ts .", "format": "prettier --ignore-path .gitignore --write '**/*.+(js|ts|json)'" }

使用访问修饰符

中的 private 修饰符仅 TypeScript 限于同一个类的可见性。在属性或方法中添加 private 修饰符后,就可以在同一类中访问该属性或方法。

public 修饰符允许从所有位置访问类属性和方法。如果您没有为属性和方法指定任何访问修饰符,则默认情况下,它们将采用 public 修饰符。

protected 修饰符允许在同一类和子类中访问类的属性和方法。当您希望在 AWS CDK 应用程序中创建子类时,请使用 protected 修饰符。

使用实用程序类型

中的@@ 实用程序类型 TypeScript 是预定义的类型函数,用于对现有类型执行转换和操作。这可以帮助您基于现有类型创建新类型。例如,您可以更改或提取属性,将属性设为可选或必填属性,或者创建类型的不可变版本。通过使用实用程序类型,您可以定义更精确的类型,并在编译时捕获潜在的错误。

部分 <Type>

Partial将输入类型的所有成员标记Type为可选。此实用程序返回一个表示给定类型的所有子集的类型。以下是 Partial 的示例。

interface Dog { name: string; age: number; breed: string; weight: number; } let partialDog: Partial<Dog> = {};

必填项 <Type>

Required恰恰相反Partial。它使输入类型的所有成员都Type不是可选的(换句话说,是必需的)。以下是 Required 的示例。

interface Dog { name: string; age: number; breed: string; weight?: number; } let dog: Required<Dog> = { name: "scruffy", age: 5, breed: "labrador", weight 55 // "Required" forces weight to be defined };