避免枚举

  1. enum HttpMethod {
  2. Get = 'GET',
  3. Post = 'POST',
  4. }
  5. const method: HttpMethod = HttpMethod.Post;
  6. method; // Evaluates to 'POST'
  1. var HttpMethod;
  2. (function (HttpMethod) {
  3. HttpMethod["Get"] = "GET";
  4. HttpMethod["Post"] = "POST";
  5. })(HttpMethod || (HttpMethod = {}));
  6. var method = HttpMethod.Post;
  7. method; // Evaluates to 'POST'

使用枚举的坏处是:
绝大部分 TypeScript 的特性都遵循了类型级别扩展的法则:要得到 JavaScript 代码,只需要去掉类型标准即可以。然而,枚举打破了这个法则。当编译一个枚举的时候,编译器会自己生成一些 JavaScript 代码。如果编译器简单删除这些代码,这些代码就不能跑了。
(译者注:这个原因是核心好处之一就是你可以通过 esbuild 而不是 tsc 完成ts 代码到 js 代码的转换,这个速度差距可能是 10-1000倍。引入 tsc 是不明智的,因为 tsc 非常非常复杂。实际上,你只用类型的话,在代码编写阶段基本也就完成了绝大部分 tsc 的事情。在最后用 esbuild 一去类型,就可以继续了。并且不引入 tsc,代表着少了一个可能出问题的地方。)
建议避免使用枚举,而用联合类型来取代它。

避免命名空间

命名空间类似 module,但是一个文件里可以有多个命名空间。例如,在一个文件里引入了不同命名空间的导出代码,以及它们对应的测试(不建议这样使用命名空间,这里只是作为一个探讨的例子):

  1. namespace Util {
  2. export function wordCount(s: string) {
  3. return s.split(/\b\w+\b/g).length - 1;
  4. }
  5. }
  6. namespace Tests {
  7. export function testWordCount() {
  8. if (Util.wordCount('hello there') !== 2) {
  9. throw new Error("Expected word count for 'hello there' to be 2");
  10. }
  11. }
  12. }
  13. Tests.testWordCount();

命名空间在实践上会造成一些问题。在上面的枚举的例子里,我们看到了 TypeScript 的类型扩展法则。通常,TypeScript 去除类型标注,留下的就是 JavaScript 的代码。
命名空间也打破了这一法则。在 namespace Util { export function wordCount ... }代码里,不能仅仅靠去除类型标注就获得 JavaScript 的代码,否则 Util.wordCount(...) 就不能工作。整个命名空间就是一个 TypeScript 的类型定义!
对于命名空间,建议就用 ESM 取代就好了。虽然创建很多文件很麻烦。但是两者能达成的效果是完全一样的。

避免装饰器(对于现在而言)

装饰器是一个可以修改和取代其他函数或者类的方法。

  1. // @sealed这个装饰器可以防止其他类继承这个类
  2. @sealed
  3. class BugReport {
  4. type = "report";
  5. title: string;
  6. constructor(t: string) {
  7. this.title = t;
  8. }
  9. }

装饰器一开始是首先在 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 的方式。
    1. class MyClass {
    2. private field1: string;
    3. #field2: string;
    4. ...
    5. }
    建议更多使用 JavaScript 的特性。