- 测试
- 开发工具
【免费下载链接】ts-jest
A Jest transformer with source map support that lets you use Jest to test projects written in TypeScript.
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)对其的定义非常明确:
The
useESMoption 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 值时有两条硬性约束:
package.json中必须包含"type": "module",二者必须配套;- 当前代码转译器仅支持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.jsYarn 用户可以使用等价的替代命令(同样兼容 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 模式运行前提外,还有两条额外要求:
package.json需包含"type": "module";- 需要自定义 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.
相关推荐
livox_ros_driver2自动连接模式工作原理:LidarInfoChangeCallback回调实战
livox_ros_driver2自动连接模式工作原理:LidarInfoChangeCallback回调实战 一、什么是自动连接模式 livox_ros_dr
测试开发工具NumPy NEP 提案体系与开发路线图完全指南:从 NEP 0 流程到 doc/neps 目录结构解析
NumPy NEP 提案体系与开发路线图完全指南:从 NEP 0 流程到 doc/neps 目录结构解析 本文是 NumPy 增强提案(NEP,NumPy En
测试开发工具ts-jest 的 ESM 支持完整指南:Jest 运行时、tsconfig 与 Jest 配置三步走
ts jest 的 ESM 支持完整指南:Jest 运行时、tsconfig 与 Jest 配置三步走 本篇技术指南以 ts jest 在 Jest 中运行 E
测试开发工具
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考