Modules: TypeScript 
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.
Enabling 
在 node.js 中启用运行时 typescript 支持有两种方式:
- 如果需要完整支持所有 typescript的语法特性,包括使用任意版本的typescript,可以使用第三方包。
- 如果只需要轻量级支持,可以使用 node.js内置的类型擦除功能。
Full TypeScript support 
可以通过第三方包来获得完整的 typescript 支持(包括对 tsconfig.json 的配置支持)。这里以 tsx 为例进行说明,当然市面上还有许多类似的库可供选择。
首先,需要使用项目中的包管理器将其安装为开发依赖。比如使用 npm:
npm install --save-dev tsx然后,可以通过以下方式运行 typescript 代码:
npx tsx your-file.ts或者,通过 node 命令运行:
node --import=tsx your-file.tsType Stripping 
Process
Added in: v22.6.0
Stability: 1.1: Active development
Default Enabled: v23.6.0(2025-01-07)。v23.6.0 版本中 默认启用 了 --experimental-strip-types 标志,node.js 将能够执行 typescript 文件而无需额外配置。在 type-stripping 中记录的支持语法存在一些限制。这个功能是实验性的,可能会发生变化。
Related Materials:
启用 --experimental-strip-types 标志,node.js 可以直接运行 typescript 文件。默认情况下,node.js 只会执行那些不包含需要转换的 typescript 特性(如枚举或命名空间)的文件。
启用这个特性后,node.js 会将 内联类型注释 替换为空白字符,且 不会执行类型检查。如果需要启用复杂类型转换(如枚举或命名空间),可以使用 --experimental-transform-types 标志,这个标志在 node.js 的 v22.7.0 版本中实验性支持。
需要注意的是,依赖于 tsconfig.json 设置的 typescript 特性
- 路径映射功能: tsconfig配置文件中的paths字段不会被转换,因此会产生错误。最接近的可用特性是子路径导入,限制是路径需要以#开头。
- 将新版本的 javascript语法转换为旧版本的功能。
是特意不被支持的。如果需要完整的 typescript 支持,请参考上文的 Full TypeScript support 说明。
类型移除特性的 设计理念是保持轻量级,通过以下两个核心策略实现这一目标:
- 有意不支持需要额外生成 javascript代码的类型语法。
- 将内联类型语法替换为空白字符。
由于上述两个核心策略,对于源码映射的影响基本是微乎其微,node.js 不需要再额外维护源映射。
当启用
--experimental-transform-types时,源映射默认开启。
虽然类型擦除功能与大多数版本的 typescript 都兼容,但建议使用 5.7 或更新版本,并使用以下 tsconfig.json 配置:
{
  "compilerOptions": {
    "target": "esnext",
    "module": "nodenext",
    "allowImportingTsExtensions": true,
    "rewriteRelativeImportExtensions": true,
    "verbatimModuleSyntax": true
  }
}Determining module system 
node.js 在 typescript 文件中同时支持 commonjs 和 esm 语法。需要注意的是,node.js 不会在这两种模块系统之间进行转换。如果代码作为 esm 模块运行,就必须使用 import 和 export 语法;如果代码作为 commonjs 运行,则必须使用 require 和 module.exports。具体规则如下:
- .ts文件的模块系统判定方式与- .js文件相同。要使用- import和- export语法,需要在最近的父级- package.json中添加- type: "module"。
- .mts文件将始终作为- esm模块运行,类似于- .mjs文件。
- .cts文件将始终作为- commonjs模块运行,类似于- .cjs文件。
- .tsx文件目前不受支持。
与 javascript 文件一样,在 import 语句和 import() 表达式中必须包含文件扩展名,例如:使用 import './file.ts',而不是 import './file'。出于向后兼容性考虑,在 require() 调用中也必须包含文件扩展名,例如:使用 require('./file.ts'),而不是 require('./file',这与在 commonjs 文件中 .cjs 扩展名必须在 require 调用中指定的要求类似。
tsconfig.json 中的 allowImportingTsExtensions 选项将允许 typescript 编译器 tsc 对包含 .ts 扩展名的导入说明符进行类型检查。
TypeScript features 
由于 node.js 只负责移除内联类型,因此任何需要将 typescript 语法替换为新的 javascript 语法的特性都会报错,除非传入 --experimental-transform-types 标志。
--experimental-transform-types 主要需要转换的特性包括:
- 枚举(Enum)
- 命名空间(namespaces)
- 传统模块(legacy module)
- 参数属性(parameter properties)
由于装饰器(Decorators)目前是 TC39 三阶段提案,并且很快就会得到 javascript 引擎的支持,因此不会被转换,并会导致解析错误。这是一个临时限制,将在未来得到解决。
此外,node.js 不会读取 tsconfig.json 文件,也不支持依赖于 tsconfig.json 设置的特性,比如 路径别名 或 将新版 javascript 语法转换为旧版标准。
Importing types without type keyword 
由于类型擦除的工作原理,使用 type 关键字对于正确擦除类型导入是必要的。如果没有 type 关键字,node.js 会将导入视为值导入,这将导致运行时错误。可以使用 tsconfig 的 verbatimModuleSyntax 选项来匹配这种行为。
以下示例将正确运行:
import type { Type1, Type2 } from './module.ts';
import { fn, type FnParams } from './fn.ts';而以下代码将导致运行时错误:
import { Type1, Type2 } from './module.ts';
import { fn, FnParams } from './fn.ts';Non-file forms of input 
类型擦除可以在 --eval 和 STDIN 中启用。模块系统将由 --input-type 决定,这与 javascript 的处理方式相同。需要注意的是,typescript 语法在 REPL、--check 和 inspect 中不受支持。
Source maps 
由于内联类型被替换为空白字符,同时没有新增其他额外的 javascript 代码,因此在堆栈跟踪中获得正确的行号不需要源码映射,所以 node.js 不会生成它们。当启用 --experimental-transform-types 时,源码映射默认是启用的。
Type stripping in dependencies 
为了避免包作者发布用 typescript 编写的包,node.js 默认会拒绝处理 node_modules 路径下文件夹中的 typescript 文件。
Paths aliases 
tsconfig 中可以通过配置 paths 来配置路径别名。
{
  "compilerOptions": {
    "baseUrl": ".",
    "paths": {
      "scripts": ["./scripts/toolbox.ts"]
    }
  }
}那么在应用中可以使用
import { toolbox } from 'scripts';来加载 monorepo 应用中的其他包。但不幸的是,node.js 并不会尊重 tsconfig 文件,因此运行时会报如下错误:
Error [ERR_MODULE_NOT_FOUND]: Cannot find package 'scripts' imported from /Users/project/demo/packages/core/index.tsnode.js 提供了原生的 子路径导入 作为替换方案,需要在 package.json 中配置:
{
  "imports": {
    "#scripts": "scripts/toolbox.ts"
  }
}然后在代码中这样使用:
import { toolbox } from '#scripts';Attention
这里需要注意的是,scripts 是应用之外的包,而子路径导入的包要么位于 node_modules 目录下,要么位于应用之内。虽然可以通过将 scripts 包手动软链到 node_modules 目录下,或者将 scripts 包软链到应用之内,但这就形成了感染链,即应用依赖的 scripts 包的所有依赖模块也必须满足这个条件。
 XiSenao
 XiSenao