避免枚举
enum HttpMethod {Get = 'GET',Post = 'POST',}const method: HttpMethod = HttpMethod.Post;method; // Evaluates to 'POST'
var HttpMethod;(function (HttpMethod) {HttpMethod["Get"] = "GET";HttpMethod["Post"] = "POST";})(HttpMethod || (HttpMethod = {}));var method = HttpMethod.Post;method; // Evaluates to 'POST'
使用枚举的坏处是:
绝大部分 TypeScript 的特性都遵循了类型级别扩展的法则:要得到 JavaScript 代码,只需要去掉类型标准即可以。然而,枚举打破了这个法则。当编译一个枚举的时候,编译器会自己生成一些 JavaScript 代码。如果编译器简单删除这些代码,这些代码就不能跑了。
(译者注:这个原因是核心好处之一就是你可以通过 esbuild 而不是 tsc 完成ts 代码到 js 代码的转换,这个速度差距可能是 10-1000倍。引入 tsc 是不明智的,因为 tsc 非常非常复杂。实际上,你只用类型的话,在代码编写阶段基本也就完成了绝大部分 tsc 的事情。在最后用 esbuild 一去类型,就可以继续了。并且不引入 tsc,代表着少了一个可能出问题的地方。)
建议避免使用枚举,而用联合类型来取代它。
避免命名空间
命名空间类似 module,但是一个文件里可以有多个命名空间。例如,在一个文件里引入了不同命名空间的导出代码,以及它们对应的测试(不建议这样使用命名空间,这里只是作为一个探讨的例子):
namespace Util {export function wordCount(s: string) {return s.split(/\b\w+\b/g).length - 1;}}namespace Tests {export function testWordCount() {if (Util.wordCount('hello there') !== 2) {throw new Error("Expected word count for 'hello there' to be 2");}}}Tests.testWordCount();
命名空间在实践上会造成一些问题。在上面的枚举的例子里,我们看到了 TypeScript 的类型扩展法则。通常,TypeScript 去除类型标注,留下的就是 JavaScript 的代码。
命名空间也打破了这一法则。在 namespace Util { export function wordCount ... }代码里,不能仅仅靠去除类型标注就获得 JavaScript 的代码,否则 Util.wordCount(...) 就不能工作。整个命名空间就是一个 TypeScript 的类型定义!
对于命名空间,建议就用 ESM 取代就好了。虽然创建很多文件很麻烦。但是两者能达成的效果是完全一样的。
避免装饰器(对于现在而言)
装饰器是一个可以修改和取代其他函数或者类的方法。
// @sealed这个装饰器可以防止其他类继承这个类@sealedclass BugReport {type = "report";title: string;constructor(t: string) {this.title = t;}}
装饰器一开始是首先在 TypeScript 添加的,然后 JavaScript(ECMAScript)才开始了标准化进程。在 2022 年1月,装饰器依然是一个 ECMAScript 提案阶段 2 的提案。阶段 2 代表在 “draft”(起草)阶段。装饰器提案似乎一直停滞在委员会中:实际上,这个提案是在 2019 年 2 月到达阶段 2 的。
我们建议在 stage 3 前,避免使用装饰器。stage 3 指 “candidate” 阶段,或者 stage 4 “finished” 阶段。
有这样的可能,即 ECMAScript 永远不完成装饰器提案。如果这个提案不完成,装饰器的处境就和枚举、命名空间一样。使用装饰器,就代表着打破了 TypeScript 的类型扩展规则,并且使用这个特性,很多打包工具,可能都是有问题的。我们不知道多会能让装饰器通过,但是装饰器带来的好处并没有那么大,所以我们选择等待。
(译者注:如果你想享受 esbuild 带来的好处,装饰器用的深就可能是个问题。当然,如果你的业务可以自闭在一套装饰器写的框架里,可能也不是非常大的问题。但是,如果 JS 的装饰器出现,现有的装饰器框架可能就有问题了。)
避免 Private 关键字
TypeScript 有两种方式让一个类型属性私有:
- 老的方法是 private 关键字,这个是 TypeScript 独有的。
- 一个新的方式:#somePrivateField,这个是 JavaScript 的方式。
建议更多使用 JavaScript 的特性。class MyClass {private field1: string;#field2: string;...}
