Skip to content

erasableSyntaxOnly

A Note to Our Readers

While maintaining fidelity to the source material, this translation incorporates explanatory content and localized expressions, informed by the translator's domain expertise. These thoughtful additions aim to enhance readers' comprehension of the original text's core messages. For any inquiries about the content, we welcome you to engage in the discussion section or consult the source text for reference.

typescript 将在 v5.8 版本中引入了一个新的编译器标志 erasableSyntaxOnly。这个标志的核心目的是为了限制开发者仅允许使用 可擦除 的语法特性。当启用这个标志时,以下三种不可擦除 的类型语法将会被标记为错误:

  1. 枚举(enums)
  2. 命名空间(namespaces)
  3. 类参数属性(class parameter properties)

例如,以下代码在启用 erasableSyntaxOnly 时,编译器都会报错:

ts
// Error! Not allowed
enum Example {
  foo
}

// Error! Not allowed
// eslint-disable-next-line @typescript-eslint/no-namespace
namespace OhNo {
  export const foo = 1;
}

class X {
  // Error! Not allowed
  constructor(private foo: string) {}
}

What Does 'erasable' Mean?

可擦除 的类型语法指的是:当我们删除这些类型语法结构时,代码的运行时行为不会受到影响。

这是一个非常重要的概念,让我们通过一个简单的例子来理解。

普通的类型注解 就是 可擦除 的:

ts
// 带类型注解的代码
const name: string = 'TypeScript';

// 删除类型注解后的代码
const name = 'TypeScript';

可以看到,删除 : string 类型注解后,代码依然是合法的 javascript,而且运行结果完全相同。

函数参数的类型注解 也是如此:

ts
// 带参数类型的函数
function greet(name: string) {
  console.log(`Hello, ${name}!`);
}

// 删除参数类型后的函数
function greet(name) {
  console.log(`Hello, ${name}!`);
}

但是 枚举命名空间类参数属性 这些类型语法并不是 可擦除 的,因为这些类型语法在编译阶段会额外生成 javascript 代码:

ts
enum Example {
  foo
}

// typescript compiler will generate the following code
var Example;
(function (Example) {
  Example[(Example['foo'] = 0)] = 'foo';
})(Example || (Example = {}));
ts
// eslint-disable-next-line @typescript-eslint/no-namespace
namespace OhNo {
  export const foo = 1;
}

// typescript compiler will generate the following code
var OhNo;
(function (OhNo) {
  OhNo.foo = 1;
})(OhNo || (OhNo = {}));
ts
class X {
  constructor(private foo: string) {}
}

// typescript compiler will generate the following code
class X {
  foo;
  constructor(foo) {
    this.foo = foo;
  }
}

构建工具需要通过 复杂的逻辑 来处理这些语法:

  • rollup 会使用 @rollup/plugin-typescript 插件来编译 typescript 模块,插件内部会使用到 typescriptcompiler api 来转译这些复杂的语法,生成额外的 javascript 代码。
  • esbuild 自身内置实现了 typescript 的类型系统,可以将复杂的 typescript 类型转换为 javascript 代码。

Why Is erasableSyntaxOnly Being Added?

引入 erasableSyntaxOnly 有几个重要原因:

Default support for Node.js

通过 type-stripping 文章的介绍中可知,node.jsv22.6.0 版本中添加了对 typescript 的类型移除的实验性支持。

但是 node.js 默认只会执行那些不需要转换的 typescript 特性(即 可擦除 的类型)的模块。在这种模式下,node.js 会将内联的 类型注解 替换为 空白字符,并且不会执行任何类型检查。

Additional Feature Support

如果需要支持更复杂的 typescript 特性转换(即 不可擦除 的类型),可以使用 --experimental-transform-types 标志,这个特性在 node.jsv22.7.0 版本中实验性支持。

需要转换的最主要特性包括:

  • Enum(枚举)
  • namespaces(命名空间)
  • legacy module(遗留模块)
  • parameter properties(参数属性)

此外,node.js 不会读取 tsconfig.json 文件,也不支持依赖于 tsconfig.json 中设置的特性,比如 路径别名将较新的 javascript 语法转换为较旧标准

可擦除语法 类型特性代表了 node.js 在提供 typescript 支持方面的一个重要进展,它提供了一个轻量级的解决方案,使开发者能够直接在 node.js 中运行 typescript 代码,同时避免了完整 typescript 编译过程的复杂性。提前启用 erasableSyntaxOnly 是一个很好的选择,通过避免使用 不可擦除 的类型特性,使得 typescript 模块可以直接运行在 node.js 环境中。

Typescript Future Prospect

这反映了 typescript 团队对未来的展望,复杂语法将来可能会被其他形式的语法替代或者是不再使用。

目前有几个提案正在讨论是否向 javascript 添加类型,其中最受欢迎的是"类型即注释"(types as comments)提案。该提案允许 javascript 引擎将类型声明视为可以在运行时忽略的注释。

比如下面的代码在未来可能成为合法的 javascript:

ts
// 带类型注解的代码
const name: string = 'TypeScript';

// 删除类型注解后的代码
const name = 'TypeScript';

这将是生态系统的重大进步,离在浏览器中直接运行 typescript 更近了一步。但是,这种方案只适用于 可擦除语法。像 枚举命名空间 这样需要复杂转换的特性就无法适配 类型即注释 特性。

What About Import Aliases?

导入别名(通过 paths 选项配置)是处理导入的一种流行方式。不幸的是,在使用 erasableSyntaxOnly 时这种方式是无法工作。

推荐的解决方案是依赖于非 typescript 方法来处理这个问题:package.json#importstypescriptnode.js 默认支持此功能。

json
{
  "imports": {
    "#dep": {
      "node": "dep-node-native",
      "default": "./dep-polyfill.js"
    }
  },
  "dependencies": {
    "dep-node-native": "^1.0.0"
  }
}

Feature Launch Time

这个特性将在不久的将来推出 typescript v5.8 版本,可以在 typescript playground 中提前尝试,对于非可擦除语法,会提示如下报错信息。

This syntax is not allowed when 'erasableSyntaxOnly' is enabled.

Contributors

Changelog

Discuss

Released under the CC BY-SA 4.0 License. (2619af4)