☰
ts-jest 的 useESM 配置:让 Jest 以 ESM 语法运行 TypeScript 测试
2026/10/7 2:21:31 网站建设 项目流程
  • 测试
  • 开发工具

【免费下载链接】ts-jest

A Jest transformer with source map support that lets you use Jest to test projects written in TypeScript.

项目地址:https://gitcode.com/gh_mirrors/ts/ts-jest
点击查看免费下载

useESM是ts-jest提供给 Jest transformer 的核心开关,它决定ts-jest在编译 TypeScript 时尽可能输出 ESM(ECMAScript Modules)语法,还是输出传统 CommonJS 语法。本文将以useESM选项为主线,讲解它的默认行为、在jest.config.ts中的完整配置方式、与 Jest ESM 运行时及tsconfig模块策略的配合方法,并结合 ts-jest 仓库源码说明该选项在底层是如何生效的。读完本文,你将能够把基于 TypeScript 的 Jest 测试工程完整切换到 ESM 模式,并处理.mts/.mjs扩展名等进阶问题。

useESM 选项是什么

useESM是ts-jesttransformer 的一个布尔配置项,官方文档(website/versioned_docs/version-29.4/getting-started/options/useESM.md)对其的定义非常明确:

TheuseESMoption allowsts-jestto transform codes to ESM syntaxif possible.

即:该选项让ts-jest在条件允许的情况下将代码转换为 ESM 语法。这里的关键词是 "if possible",它意味着useESM并不是一个"无条件强制输出 ESM"的开关,而是需要与 Jest 的 ESM 运行时能力配合才能生效(下文"底层原理"一节会详细说明)。

在 src/types.ts 中可以看到该选项的类型定义与注释:

/** * Tell `ts-jest` to transform codes to ESM format. This only works in combination with `jest-runtime` ESM option * `supportsStaticESM` true which is passed into Jest transformer */ useESM?: boolean

类型注释再次强调:该选项只有在 Jest 运行时通过supportsStaticESM: true告知 transformer 支持静态 ESM 时才真正起作用。

默认值:false,输出 CommonJS

文档明确说明useESM的默认值是false,此时ts-jest会把代码转换为CommonJS语法。这在 src/legacy/config/config-set.ts 的配置类字段声明和 src/legacy/config/config-set.ts 的解析逻辑中都有印证:

// 字段默认值 useESM = false // 配置解析:未显式提供时回退为 false this.useESM = options.useESM ?? false

因此,如果项目没有任何 ESM 相关配置,ts-jest默认行为就是把 TS 编译成 CommonJS 模块,这也是绝大多数传统 Jest 工程的默认形态。

完整配置示例

在jest.config.ts中开启useESM的最小配置如下(取自官方文档原始示例):

import type { Config } from 'jest' const jestConfig: Config = { // [...] transform: { // '^.+\\.[tj]sx?$' to process ts,js,tsx,jsx with `ts-jest` // '^.+\\.m?[tj]sx?$' to process ts,js,tsx,jsx,mts,mjs,mtsx,mjsx with `ts-jest` '^.+\\.tsx?$': [ 'ts-jest', { useESM: true, }, ], }, } export default jestConfig

注意示例中两行注释的含义:

  • '^.+\\.[tj]sx?$':匹配.ts、.js、.tsx、.jsx(不包含.mts/.mjs等带m前缀的扩展名);
  • '^.+\\.m?[tj]sx?$':在上一模式基础上额外匹配.mts、.mjs、.mtsx、.mjsx,这是 ESM 场景下更推荐使用的 transform 模式。

这两个正则模式在 src/constants.ts 中都有对应的具名常量,例如ESM_TS_TRANSFORM_PATTERN = '^.+\\.m?tsx?$',建议在实际工程中直接导入这些常量以避免手写正则出错。

"if possible" 的底层原理:supportsStaticESM

useESM: true只是"尽力而为",真正决定输出 ESM 还是 CommonJS 的是useESM与 Jest 传入的supportsStaticESM标志的组合判断。在 src/legacy/compiler/ts-compiler.ts 中可以看到核心逻辑:

getCompiledOutput(fileContent: string, fileName: string, options: TsJestCompileOptions): CompiledOutput { const isEsmMode = this.configSet.useESM && options.supportsStaticESM this._compilerOptions = this.fixupCompilerOptionsForModuleKind(this._initialCompilerOptions, isEsmMode) // ... }

也就是说,只有当useESM为 true且Jest 运行时声明支持静态 ESM(supportsStaticESM: true)时,ts-jest才会进入 ESM 编译模式;否则即使配置了useESM: true,最终也会回退为 CommonJS 输出。

在 legacy 转换器 src/legacy/ts-jest-transformer.ts 中,这一判断还会进一步影响传给 TypeScript 的module取值以及.mjs文件名的保留策略:

compilerOptions: { ...configs.parsedTsConfig.options, module: transformOptions.supportsStaticESM && transformOptions.transformerConfig?.useESM ? ts.ModuleKind.ESNext : ts.ModuleKind.CommonJS, }, // .mjs fileName causes ts.transpileModule to preserve ESM syntax even with module: CommonJS fileName: transformOptions.supportsStaticESM && transformOptions.transformerConfig?.useESM ? sourcePath : sourcePath.replace(/\.mjs$/, '.js'),

从源码可以推断:当 ESM 模式激活时,ts-jest会把module强制为ESNext,并保留.mjs文件名以保证transpileModule输出原生 ESM 语法。

如何让 supportsStaticESM 为 true

supportsStaticESM是 Jest 运行时通过--experimental-vm-modules标志启用 ESM 支持后,在 transformer 调用链中自动传入的。也就是说,只改jest.config.ts是不够的,还必须以 ESM 模式启动 Jest(详见下文"启动 Jest ESM 运行时"一节)。这一点也是文档中"See more about ESM support in dedicated guide"提示(指向 website/docs/guides/esm-support.md)所强调的前提。

配套 tsconfig 配置

开启useESM: true后,还需要保证tsconfig的module配置与 ESM 目标一致。esm-support 指南给出了两类可选方案(二选一):

方案一:使用 ES 系列 module 值

{ "compilerOptions": { "module": "ES2022", // or `ESNext` "target": "ESNext", "esModuleInterop": true } }

可选值包括ES2015、ES2020、ES2022、ESNext等。指南特别建议优先使用ES2022或ESNext,以完整支持近年 ESM 新特性(如顶层 await、import 属性等)。

方案二:使用 hybrid 模块值(Node16/Node18/NodeNext)

{ "compilerOptions": { "module": "Node16", // or Node18/NodeNext "target": "ESNext", "esModuleInterop": true, "isolatedModules": true } }

使用 hybrid 值时有两条硬性约束:

  1. package.json中必须包含"type": "module",二者必须配套;
  2. 当前代码转译器仅支持hybrid 值配合isolatedModules: true使用(这是 ts-jest 在 website/docs/guides/esm-support.md 中明确标注的限制)。

启动 Jest ESM 运行时

由于 Jest 原生以 CommonJS 方式运行,要真正让useESM生效,必须用 Node 的实验性 VM 模块标志启动 Jest:

node --experimental-vm-modules node_modules/jest/bin/jest.js

Yarn 用户可以使用等价的替代命令(同样兼容 Yarn Plug'n'Play):

yarn node --experimental-vm-modules $(yarn bin jest)

如果 Jest 配置文件本身用 TypeScript 编写,还需要安装ts-node作为开发依赖:

npm install -D ts-node

从源码测试用例(如 src/legacy/ts-jest-transformer.spec.ts)可以看出,测试中正是通过传入supportsStaticESM: true与transformerConfig: { useESM: true }的组合来驱动 ESM 编译路径的,这与运行时行为一致。

使用 ESM presets 简化配置

除了手动在transform中写useESM: true,更推荐使用 ts-jest 提供的 ESM preset 工厂函数。完整 preset 列表见 website/versioned_docs/version-29.4/getting-started/presets.md。

import type { Config } from 'jest' import { createDefaultEsmPreset } from 'ts-jest' const presetConfig = createDefaultEsmPreset({ //...options }) export default { ...presetConfig, } satisfies Config

在 src/presets/create-jest-preset.ts 中可以确认:ESM preset 内部会自动设置useESM: true,并在返回配置中附带extensionsToTreatAsEsm(src/constants.ts 中定义为['.ts', '.tsx', '.mts'])。对应的预设类型定义见 src/types.ts,其 transform 模式为:

export type DefaultEsmPreset = { extensionsToTreatAsEsm: string[] transform: { [ESM_TS_TRANSFORM_PATTERN]: ['ts-jest', { useESM: true } & DefaultEsmTransformOptions] } }

如果不使用 preset,则需手动补齐extensionsToTreatAsEsm与 ESM transform 模式:

import type { Config } from 'jest' import { TS_EXT_TO_TREAT_AS_ESM, ESM_TS_TRANSFORM_PATTERN } from 'ts-jest' export default { extensionsToTreatAsEsm: [...TS_EXT_TO_TREAT_AS_ESM], transform: { [ESM_TS_TRANSFORM_PATTERN]: [ 'ts-jest', { //...other `ts-jest` options useESM: true, }, ], }, } satisfies Config

仓库的 E2E 测试给出了一个真实的落盘示例:e2e/esm-features/jest-compiler-esm.config.ts 中同时设置了extensionsToTreatAsEsm: ['.ts']与useESM: true,并配合module: ESNext的 tsconfig(e2e/esm-features/tsconfig-esm-transpiler.spec.json)。

解析 .mjs / .mts 扩展名

要使用.mts扩展名,除了满足 ESM 模式运行前提外,还有两条额外要求:

  1. package.json需包含"type": "module";
  2. 需要自定义 Jest resolver,把.mjs请求解析到对应的.mts文件,例如:
import type { SyncResolver } from 'jest-resolve' const mjsResolver: SyncResolver = (path, options) => { const mjsExtRegex = /\.mjs$/i const resolver = options.defaultResolver if (mjsExtRegex.test(path)) { try { return resolver(path.replace(mjsExtRegex, '.mts'), options) } catch { // use default resolver } } return resolver(path, options) } export default mjsResolver

然后在 Jest 配置中挂载:

import type { Config } from 'jest' const config: Config = { //...other options resolver: '<rootDir>/path/to/custom-resolver.ts', }

另外,从 src/legacy/ts-jest-transformer.ts 的源码可以看到,.mjs文件名在 ESM 模式下会被原样保留传给transpileModule,这是保证输出仍为 ESM 语法的关键细节。

pathsToModuleNameMapper 的 useESM 选项

useESM还以参数形式出现在 ts-jest 的路径映射工具pathsToModuleNameMapper中(src/config/paths-to-module-name-mapper.ts):

export const pathsToModuleNameMapper = ( mapping: TsPathMapping, { prefix = '', useESM = false }: { prefix?: string; useESM?: boolean } = {}, ): JestPathMapping => {

当useESM: true时,该工具会额外生成两类映射规则(见 src/config/paths-to-module-name-mapper.ts 与 src/config/paths-to-module-name-mapper.ts):

  • 为每个路径别名追加.js后缀匹配模式(如^@alias/(.*)\.js$),以兼容 ESM 下必须带扩展名的导入写法;
  • 追加'^(\\.{1,2}/.*)\\.js$' → '$1'规则,将相对路径导入中的.js后缀剥离回真实源文件。

对应测试见 src/config/paths-to-module-name-mapper.spec.ts,其中明确验证了useESM: true时会为 resolved config 追加js扩展名的映射。

E2E 验证:ESM 特性实测

仓库在e2e/esm-features目录下提供了针对 ESM 能力的端到端测试,测试用例 e2e/esm-features/tests/esm-features.spec.ts 覆盖了典型 ESM 特性:

  • import.meta元属性访问;
  • JSON 模块的 import 断言(with { type: 'json' });
  • 动态导入与 import 属性;
  • 顶层 await(top-level await)。

这些用例只有在useESM: true且 Jest 以 ESM 模式运行时才能通过,是验证整条 ESM 链路是否配置正确的直接手段。

小结与注意事项

  • useESM默认值为false,输出 CommonJS;设为true后ts-jest会在条件允许时输出 ESM 语法。
  • "条件允许"意味着必须同时满足:Jest 以--experimental-vm-modules启动(从而传入supportsStaticESM: true)以及tsconfig的module配置为 ES 系列值或 hybrid 值(后者需配合"type": "module"与isolatedModules: true)。
  • 使用createDefaultEsmPreset等 preset 工厂函数可自动生成useESM: true+extensionsToTreatAsEsm+ ESM transform 模式的组合配置,推荐优先采用。
  • 若涉及.mts/.mjs,还需"type": "module"与自定义 resolver 配合;路径别名场景下可结合pathsToModuleNameMapper的useESM: true参数生成兼容 ESM 的映射。
  • 更多完整的 ESM 配置示例可参考仓库 examples 目录下的jest-esm.config.ts、tsconfig-esm.json等文件,以及官方 ESM 指南 website/docs/guides/esm-support.md。
  • 测试
  • 开发工具

【免费下载链接】ts-jest

A Jest transformer with source map support that lets you use Jest to test projects written in TypeScript.

项目地址:https://gitcode.com/gh_mirrors/ts/ts-jest
点击查看免费下载
上一篇:如何用DownKyi哔哩下载姬高效管理B站视频:终极免费解决方案
下一篇:DownKyi哔哩下载姬:免费高效的B站视频下载终极指南

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询