☰
用 ts-jest 在 Jest 中测试 TypeScript 项目:带 Source Map 支持的类型检查型 Transformer 全解析
2026/10/7 16:09:16 网站建设 项目流程
  • 测试
  • 开发工具

【免费下载链接】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
点击查看免费下载

ts-jest是一个带 Source Map 支持的 Jest transformer)为核心骨架,结合仓库源码与配套文档,为你梳理 ts-jest 的能力边界、与 Babel 方案的取舍、快速上手步骤、核心配置以及底层处理流程,读完后你将能独立完成 TypeScript 项目的 Jest 测试环境搭建与调优。

ts-jest 是什么

根据项目官方定位(见 README.md 与 website/docs/introduction.md):

ts-jest 是一个带 Source Map 支持的 Jest transformer,它让你可以使用 Jest 测试用 TypeScript 编写的项目。

围绕这一定位,有三个关键能力值得展开:

  1. 它是一个 Jest transformer:在 Jest 的代码转换链路中,ts-jest 通过 Jest 的transform配置被挂载到.ts/.tsx等文件的处理流程上,负责把这些文件编译成 Jest 可以执行的 JavaScript。
  2. 带 Source Map 支持:编译产物会携带 Source Map,因此当测试失败或抛出异常时,Jest 能够把堆栈信息映射回原始的.ts源文件,错误定位准确、调试体验友好。
  3. 支持 TypeScript 的全部特性,包括类型检查:这是 ts-jest 区别于纯转译方案(如 Babel)的核心卖点——它不只是"去掉类型注解",而是真正以 TypeScript 编译器 API 驱动,在测试运行的同时进行类型检查(见 website/docs/babel7-or-ts.md)。

一个值得注意的细节是:ts-jest 项目自身也使用 ts-jest 来测试自己(README 的 "Built With" 一节明确写了ts-jest uses itself for its tests)。这意味着该项目每天都要在"自己编译自己、自己测试自己"的场景下运行,其 transformer 的正确性与健壮性由项目自身的测试套件背书。

入口与实现:一个 "createTransformer" 的包

从源码结构看,ts-jest 的对外入口非常简洁。在 src/index.ts 中,包默认导出了一个包含createTransformer(tsJestConfig?)方法的对象,该方法返回TsJestTransformer实例:

export default { createTransformer(tsJestConfig?: TsJestTransformerOptions) { return new TsJestTransformer(tsJestConfig) }, }

TsJestTransformer的核心实现位于 src/legacy/ts-jest-transformer.ts,它实现了 Jest 的SyncTransformer接口,提供了process/processAsync/getCacheKey/getCacheKeyAsync等方法,并在构造时缓存ConfigSet、维护编译器实例与依赖图(depGraphs),从而保证同一测试进程中多次转换可以复用编译结果。

为什么需要 transformer:Jest 并不原生理解 TypeScript

Jest 本身只能直接执行 JavaScript。对于.ts/.tsx文件,Jest 需要借助 transformer 在require()之前将其转换为 JavaScript。ts-jest 正是承担这一职责的官方推荐的 TypeScript transformer。

安装并配置好 ts-jest 后,Jest 对每个文件的处理流程大致是:

  1. 判断该文件是否有对应的 transform 规则;
  2. 若 transformer 提供了getCacheKey,则用它计算缓存键;
  3. 若命中缓存则直接使用缓存内容,否则调用transformer.process(...)编译文件并更新缓存;
  4. 最终require()编译后的产物。

这一流程的详细 PlantUML 描述见仓库内部文档 website/docs/processing.md。

ts-jest 与 Babel7 + @babel/preset-typescript 的对比

2018 年 9 月 Babel 7 发布,带来了@babel/preset-typescript,目标是让 Babel 用户无需迁移即可通过添加一个 preset 来尝试 TypeScript。那么既然 Babel 也能处理 TypeScript 语法,为什么还需要 ts-jest?

官方文档 website/docs/babel7-or-ts.md 系统性地列出了@babel/preset-typescript的局限——以下这些 TypeScript(与 ts-jest)能做到、而 Babel 做不到的事情,正是选择 ts-jest 的关键理由。

没有类型检查

这是使用 TypeScript(而非 Babel)的最大优势:开箱即用的类型检查。

在使用 ts-jest 时,文件在编译的同时会被类型检查,从而获得更流畅的 TDD(测试驱动开发)体验。下面这段代码,TypeScript 会直接报错,而 Babel 则不会:

const str: string = 42

Babel 将文件当作相互孤立的模块逐一转译,不存在"项目"的概念;而 TypeScript 中文件属于一个项目(project),是在项目作用域内统一编译的。这意味着跨文件的类型关系、import类型推断等都能被正确校验。

不支持namespace

namespace app { export const VERSION = '1.0.0' export class App { /* ... */ } }

Babel 的preset-typescript无法处理这种命名空间语法。

不支持const enum

const enum Directions { Up, Down, Left, Right, }

const enum会被 TypeScript 编译器内联展开,Babel 按文件独立转译时无法完成这种跨文件的内联。

不支持声明合并

Babel 无法处理enum、namespace等类型的声明合并(declaration merging)特性。

不支持 legacyimport/export

import lib = require('lib') // ... export = myVar

这类历史遗留的导入导出语法同样超出 Babel preset 的能力范围。

开启 JSX 时不支持尖括号类型断言

const val = <string>input

当 TSX/JSX 开启时,尖括号会被解析为 JSX 语法,Babel 无法把它当作类型断言处理,而 ts-jest 基于 TypeScript 编译器则没有这个问题。

总结:如果你的项目用到了上述任一特性,或者希望在测试运行过程中同步获得类型检查保障,ts-jest 是比 Babel +preset-typescript更完整的选择。

快速开始:安装、配置与运行

以本仓库官方文档 website/docs/getting-started/installation.md 为准,最快三步即可跑通。

1. 安装依赖

将 jest、typescript、ts-jest 与 @types/jest 一次性安装为开发依赖:

npm install --save-dev jest typescript ts-jest @types/jest

如果你的项目使用 TypeScript 7,不要直接安装typescript,而应遵循官方推荐的并排(side-by-side)编译器方案,详见 website/docs/guides/typescript-7.md。

2. 创建 Jest 配置

默认情况下 Jest 无需任何配置文件即可运行,但它不会编译.ts文件。要让 Jest 使用 ts-jest 转译 TypeScript,需要创建一份告知 Jest 使用 ts-jest preset 的配置文件。

ts-jest 可以自动生成配置文件:

npx ts-jest config:init

使用 yarn 时对应:

yarn ts-jest config:init

该命令会创建一份基础的 Jest 配置文件,告知 Jest 如何正确处理.ts文件。如果你在安装时遇到npx: command not found之类的报错,可以把npx XXX替换为node node_modules/.bin/XXX(在项目根目录下执行)。

此外,你也可以使用create-jest命令(同样以npx或yarn前缀)来获得更多 Jest 相关选项;但对其中的 TypeScript 询问要回答no,然后在生成的jest.config.js中手动加入一行:preset: "ts-jest"。

官方给出的全流程速查表如下:

步骤npmyarn
前置依赖(TypeScript 4.3–6)npm i -D jest typescriptyarn add --dev jest typescript
安装npm i -D ts-jest @types/jestyarn add --dev ts-jest @types/jest
创建配置npx ts-jest config:inityarn ts-jest config:init
运行测试npm test或npx jestyarn test或yarn jest

3. 运行测试

直接执行npm test或npx jest即可。此时.ts/.tsx文件会被 ts-jest 转译,且转换产物携带 Source Map,错误堆栈会指向原始 TypeScript 源码。

相关配置入口

如需进一步定制 Jest,请参考 Jest 官方配置指南;ts-jest 特有的配置项汇总在 website/docs/getting-started/options.md。若使用 ESM 项目,还需要阅读 website/docs/guides/esm-support.md。

核心配置选项一览

所有 ts-jest 专属选项都可以定义在 Jest 的transform配置对象中,既可以写在package.json里,也可以通过jest.config.js或jest.config.ts文件提供。

当使用 TypeScript 编写 Jest 配置文件时,Jest 会借助ts-node来编译该配置文件,ts-jest 不参与这一过程(见 website/docs/getting-started/options.md 的注意事项)。

官方列出的选项总表如下:

选项说明类型默认值
compiler用作编译器的 TypeScript 模块string"typescript"
tsconfigTypeScript 编译相关配置string|object|booleanauto
isolatedModules关闭类型检查(按隔离模块编译)booleandisabled
astTransformers自定义 TypeScript AST 转换器objectauto
diagnostics诊断信息相关配置boolean|objectenabled
babelConfigBabel(Jest) 相关配置boolean|string|objectdisabled
stringifyContentPathRegex匹配的文件将变成返回自身内容的模块string|RegExpdisabled
useESM启用 ESM 支持booleanauto

注意:如果你使用自定义的transform配置,请从 Jest 配置中移除preset,避免 Jest 不能正确转换文件(官方文档明确警告过这一点)。

tsconfig 选项的三种用法

tsconfig选项允许你指定要使用的tsconfigJSON 文件,也可以直接内联一份 compilerOptions 对象(详见 website/docs/getting-started/options/tsconfig.md)。

默认情况下,ts-jest 会在你的项目中查找tsconfig.json;若找不到,则使用 TypeScript 默认的 compilerOptions,唯一例外是target使用ES2015而不是默认的ES5。如果你希望即使项目里存在tsconfig.json也强制使用默认值,可以将该选项设为false。

指定配置文件路径(路径相对于启动 Jest 的目录,也支持<rootDir>前缀):

import type { Config } from 'jest' const jestConfig: JestConfigWithTsJest = { // [...] transform: { // '^.+\\.[tj]sx?$' 处理 ts,js,tsx,jsx // '^.+\\.m?[tj]sx?$' 处理 ts,js,tsx,jsx,mts,mjs,mtsx,mjsx '^.+\\.tsx?$': [ 'ts-jest', { tsconfig: 'tsconfig.test.json', }, ], }, } export default jestConfig

内联 compilerOptions(等同于写在tsconfig.json的compilerOptions里的对象):

const jestConfig: Config = { // [...] transform: { '^.+\\.tsx?$': [ 'ts-jest', { tsconfig: { importHelpers: true, }, }, ], }, }

禁用自动查找:

const jestConfig: Config = { // [...] transform: { '^.+\\.tsx?$': [ 'ts-jest', { tsconfig: false, }, ], }, }

isolatedModules:以性能换取类型检查

默认情况下 ts-jest 在"项目"上下文中使用 TypeScript 编译器,具备完整的类型检查与全部特性。但它也可以把每个文件当作独立的"隔离模块"分别编译,这就是isolatedModules选项(默认false)的作用(详见 website/docs/getting-started/options/isolatedModules.md)。

开启后会失去类型检查能力,以及const enum等特性;但在配合jest --no-cache运行时,测试会明显更快:

const jestConfig: Config = { // [...] transform: { '^.+\\.tsx?$': [ 'ts-jest', { isolatedModules: true, }, ], }, }

注意:该配置页已被标记为 DEPRECATED,官方建议改用tsconfig.json中的isolatedModules选项,未来大版本中会移除 ts-jest 侧的isolatedModules配置。

性能提示与注意事项

使用isolatedModules: false(即开启类型检查)时性能相对更慢,可以通过收窄tsconfig.json中include的文件范围来提升性能——include提供的文件越少,测试运行能获得的性能提升越大:

{ // ...其他配置 "include": ["my-typings/*", "my-global-modules/*"] }

但代价是 ts-jest 可能无法识别所有打算配合 Jest 使用的文件,自定义类型、全局模块等可能出问题。官方给出的建议是:让测试环境真正需要的文件被include的 glob 模式覆盖到,从而兼顾性能提升与不破坏现有行为。

预设(Presets)与编程式配置

Jest 中的 preset 是预定义的配置,用来标准化测试环境的搭建。ts-jest 提供了多组高度定制化的 preset,官方推荐的最佳实践是调用 preset 创建函数来生成(并可扩展)配置,而将旧的字符串形式 preset 视为 legacy 方案(详见 website/docs/getting-started/presets.md)。

以最常用的createDefaultPreset为例(其实现见 src/presets/create-jest-preset.ts):

import { createDefaultPreset, type JestConfigWithTsJest } from 'ts-jest' const presetConfig = createDefaultPreset({ //...options }) const jestConfig: JestConfigWithTsJest = { ...presetConfig, } export default jestConfig

createDefaultPreset(options)返回一个包含transform属性的对象,将'^.+.tsx?$'映射到['ts-jest', TsJestTransformerOptions]。可选参数包括tsconfig、isolatedModules、compiler、astTransformers、diagnostics、stringifyContentPathRegex,每个参数都有独立的官方说明页(见 website/docs/getting-started/options 下的子页面)。

除默认 preset 外,ts-jest 还提供:

  • createDefaultLegacyPreset:legacy 版默认配置,transform 指向ts-jest/legacy;
  • createDefaultEsmPreset:ESM 配置,处理.ts/.mts/.tsx/.mtsx,并额外设置extensionsToTreatAsEsm与useESM: true;
  • createJsWithTsPreset:同时处理 JS 与 TS 文件(.js/.jsx/.ts/.tsx);
  • createJsWithTsEsmPreset:ESM 版 JS+TS 配置;
  • createJsWithBabelPreset及 ESM/legacy 变体:TS 文件交给 ts-jest,JS 文件交给babel-jest。

从源码可以看到这些函数的真实返回结构,例如createJsWithBabelPreset在 src/presets/create-jest-preset.ts 中同时设置了[JS_TRANSFORM_PATTERN]: 'babel-jest'与[TS_TRANSFORM_PATTERN]: ['ts-jest', ...]两条规则。

旧式字符串 preset(如ts-jest/presets/default、ts-jest/presets/js-with-ts等)仍可使用,但官方明确警告:ts-jest 不推荐使用 legacy preset,因为这种方式不利于灵活配置 Jest,且会在下个大版本中移除,用户被强烈建议迁移到上述函数式写法。

底层处理流程:从源文件到可执行代码

如果你关心 ts-jest 内部到底做了什么,仓库内部文档 website/docs/processing.md 给出了完整的处理流程(该文档定位为贡献者内部文档)。结合 src/legacy/ts-jest-transformer.ts 的实现,整个转换链路可以概括为:

  1. 入口tsJest.process(source):Jest 调用 transformer 的process方法,传入源文件内容与转换配置。
  2. 可选的字符串化:若命中stringifyContentPathRegex,文件内容会被 JSON 序列化为模块(常用于把.json、模板等文件直接变成返回自身内容的模块)。
  3. .d.ts声明文件直接清空内容:定义文件无需编译,直接产出空内容。
  4. 编译器选择:若未开启隔离模块,则创建并缓存 TypeScript Language Service,以"项目"上下文编译;若开启,则逐文件用transpileModule独立编译。
  5. 持久缓存检查:命中持久缓存则直接恢复内存缓存,否则执行编译。
  6. 自定义 AST transformers:在这里执行jest.mock的提升(hoisting)以及用户基于配置定义的 AST 转换(对应astTransformers选项)。
  7. 修复 Source Map:对编译产物修正 Source Map,更新内存缓存与持久缓存。
  8. 可选 Babel 二次处理:若配置了babelConfig,调用babel-jest的 process 再次转换。
  9. afterProcess钩子:若存在该钩子且返回了内容,则以其返回值作为新源码。

从 src/legacy/ts-jest-transformer.ts 可以看到,TsJestTransformer实现了 Jest 的SyncTransformer,并通过静态缓存_cachedConfigSets在不同测试运行之间复用ConfigSet与编译器实例,这正是 ts-jest 在 watch 模式下依然能保持较快编译速度的实现基础。

版本策略与注意事项

ts-jest不采用语义化版本(SemVer)。官方在 website/docs/introduction.md 中特别强调:

我们不进行语义化版本管理,23.10是一次重写。运行npm i -D ts-jest@"<23.10.0"可以回退到之前的版本。

这意味着从旧版本升级到23.10及以上时,需要留意可能的行为变化;如果需要兼容旧行为,可以用上面的命令锁定<23.10.0版本。项目主版本号跟随 Jest 的主版本,但整体版本策略并非严格 SemVer(详见 README.md 的 Versioning 一节)。

另外,website/docs/getting-started/version-checking.md 记载了一个已被标记 DEPRECATED 的版本检查机制:ts-jest 默认支持一个范围的 jest/typescript 版本,使用不兼容版本时会收到警告,可通过设置环境变量TS_JEST_DISABLE_VER_CHECKER=true关闭(Linux/macOS 用export,Windows 用set)。该机制已废弃,未来将由package.json中原生的peerDependencies检查取代。

TypeScript 7 用户的特别说明

如果你的项目使用 TypeScript 7,官方建议采用并排编译器方案(详见 website/docs/guides/typescript-7.md):TypeScript 7.0 自带原生tsc,但不提供 ts-jest 转换所需的 JavaScript 编译器 API。应通过 npm 别名同时安装两个编译器:

npm install --save-dev '@typescript/native@npm:typescript@^7.0.2' 'typescript@npm:@typescript/typescript6@^6.0.2'

这样npx tsc运行原生 TypeScript 7 编译器,而 ts-jest 通过typescript别名获得其需要的 TypeScript 6 JavaScript API。不要将 ts-jest 的compiler选项指向@typescript/native,因为 TypeScript 7.0 没有兼容的 JavaScript API,ts-jest 会在加载该包时提前停止并给出可操作的配置错误。

小结

ts-jest 以"带 Source Map 支持的 Jest transformer"为定位,提供了 Babel 方案所不具备的完整类型检查与全部 TypeScript 特性支持。通过本文你可以:

  • 理解 ts-jest 与 Babel7 +@babel/preset-typescript的取舍(类型检查、namespace、const enum、声明合并、legacy 导入导出、JSX 下尖括号断言等);
  • 按速查表完成安装、config:init配置与测试运行;
  • 掌握tsconfig、isolatedModules、diagnostics、astTransformers等核心选项及其默认行为;
  • 了解底层编译流程与缓存机制,为性能调优和问题排查打基础;
  • 明确非 SemVer 版本策略与 TypeScript 7 的并排安装方案。

需要继续深入时,可依次阅读仓库中的 website/docs/getting-started/installation.md、website/docs/getting-started/options.md、website/docs/getting-started/presets.md 以及 website/docs/processing.md,并结合 src/legacy/ts-jest-transformer.ts 与 src/presets/create-jest-preset.ts 阅读源码实现。

  • 测试
  • 开发工具

【免费下载链接】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
点击查看免费下载
上一篇:OSS-Fuzz 术语指南:ClusterFuzz、Fuzz Target、Job Type、Sanitizer 与架构体系详解
下一篇:Knip扫描规则原理:如何自定义检测逻辑适配业务需求

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

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

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

立即咨询