
Jest 类型系统探秘jest/types 共享类型包与类型化配置实战【免费下载链接】jestDelightful JavaScript Testing.项目地址: https://gitcode.com/gh_mirrors/je/jestjest/types是 Jest 仓库中所有子包共享的类型定义中枢它把配置、全局测试 API、测试结果、Circus 事件与转换器等相关类型统一收纳供jest-config、jest-runtime、jest-circus等内部包和外部使用者引用。本文将以该包为核心讲解如何用它对jest.config.ts做静态类型检查、如何在测试文件中获得完整的全局 API 类型提示并结合仓库源码剖析其五个类型命名空间Config、Global、TestResult、Circus、TransformTypes的内部结构与实际用途。读完本文你将掌握 Jest 类型体系的脉络并能写出类型安全、可自动补全的 Jest 配置与测试代码。一、jest/types 是什么Jest 内部类型的中枢在 packages/jest-types/README.md 的开头官方对它的定位只有一句话This package contains shared types of Jests packages本包包含 Jest 各包的共享类型。这句话点明了它的核心价值——它不是给某个特定 API 服务的独立类型库而是整个 Jest 生态共享的“类型底座”。从仓库的实际依赖关系可以印证这一点jest-config的 Defaults.ts、Descriptions.ts、jest-runtime的 index.ts 等都通过import type {Config} from jest/types引用这里的类型。可以说jest/types是 Jest 各包之间传递数据结构尤其是配置对象、测试结果、事件流时共同遵守的“类型契约”。该包当前版本为 30.4.1见 packages/jest-types/package.json运行时本身不输出任何可执行逻辑只提供.d.ts类型声明。它的依赖也完全是类型层面的jest/pattern、jest/schemas、types/istanbul-lib-coverage、types/istanbul-reports、types/node、types/yargs与chalk的类型定义。二、类型全景五大命名空间各司其职jest/types的入口文件 packages/jest-types/src/index.ts 只做了一件事聚合导出五个命名空间import type * as Circus from ./Circus; import type * as Config from ./Config; import type * as Global from ./Global; import type * as TestResult from ./TestResult; import type * as TransformTypes from ./Transform; export type {Circus, Config, Global, TestResult, TransformTypes};这五个命名空间的分工如下命名空间对应源码文件覆盖内容Configsrc/Config.ts用户配置InitialOptions、项目配置ProjectConfig、全局配置GlobalConfig、默认值DefaultOptions、CLI 参数Argv等Globalsrc/Global.tsdescribe/it/test/beforeEach等测试框架全局 API 的函数签名以及TestFn、DoneFn、Each等可复用类型TestResultsrc/TestResult.ts断言结果AssertionResult、可序列化错误SerializableError等测试产物类型Circussrc/Circus.tsJest 默认测试运行器 Circus 的同步/异步事件流、State、TestEntry、DescribeBlock等内部状态类型TransformTypessrc/Transform.ts代码转换器的调用方选项、缓存键选项与转换结果类型Config是其中体量最大、外部使用者接触最多的命名空间下一节重点展开。三、实战核心用 Config 命名空间编写类型化 jest.config.tsJest 本身开箱即用并不提供配置文件的类型声明README 明确指出“Another use-case fortypes/jestis a typed Jest config as those types are not provided by Jest out of the box”。也就是说想要让jest.config.ts获得类型检查与自动补全需要显式引入类型。3.1 最简用法README 给出了最标准的写法——从jest/types导入Config命名空间并将配置对象标注为Config.InitialOptions// jest.config.ts import type {Config} from jest/types; const config: Config.InitialOptions { // some typed config }; export default config;InitialOptions是面向用户的完整配置形态它并非凭空定义而是从jest/schemas重新导出见 src/Config.ts。类型标注之后编辑器会立即对每个配置项做校验和提示写错选项名、给数值型选项传字符串、拼错fakeTimers子选项都会在编译期直接报错。3.2 从源码看配置类型的分层设计打开 packages/jest-types/src/Config.ts 可以发现配置类型并非只有一个InitialOptions而是按生命周期分层InitialOptions用户输入面向开发者书写的宽松配置由jest/schemas提供InitialOptionsWithRootDirInitialOptions与必选的rootDir的组合src/Config.ts用于已经确定项目根目录的场景InitialProjectOptions在多项目projects场景下从InitialOptions中挑选出属于单项目的那部分字段src/Config.tsProjectConfig归一化后传给单个项目执行链路的完整配置字段全部必填且形态已规整如moduleNameMapper变成Array[string, string]、transform变成三元组数组见 src/Config.tsGlobalConfig跨项目共享的全局运行配置bail、maxWorkers、watch、seed、shard等见 src/Config.tsDefaultOptions记录所有选项的默认值字段名与ProjectConfig一一对应src/Config.ts它在jest-config的Defaults模块中驱动默认值合并逻辑Argv基于yargs的Arguments类型推导出的全部 CLI 命令行参数形态src/Config.tsjest --bail、--runInBand、--shard等参数都能在这里找到对应字段。这种分层设计保证了用户写配置时保持宽松灵活而内部各包如jest-runtime、jest-runner拿到的则是字段齐全、类型严格的ProjectConfig。3.3 用类型测试用例反推配置写法仓库在 packages/jest-types/typetests/config.test.ts 中提供了基于 tstyche 的类型级测试可以直接当作“类型安全配置”的参考样例。例如coverageThreshold支持按路径细分阈值也支持global全局兜底const config: Config {}; test(coverageThreshold, () { expect(config).type.toBeAssignableFrom({ coverageThreshold: { ./src/api/very-important-module.js: { branches: 100, functions: 100, lines: 100, statements: 100, }, ./src/components/: { branches: 40, statements: 40, }, ./src/reducers/**/*.js: { statements: 90, }, global: { branches: 50, functions: 50, lines: 50, statements: 50, }, }, }); });对应源码中CoverageThresholdValuebranches/functions/lines/statements 均为可选数值见 src/Config.ts与CoverageThresholdglobal必选、其余路径可选见 src/Config.ts的定义。该测试还展示了fakeTimers的类型约束advanceTimers可为boolean | numberdoNotFake只能填FakeableAPI列表中列出的 API 名称Date、hrtime、nextTick、performance、queueMicrotask、setTimeout等见 src/Config.ts并且新旧两套 fake timers 配置互斥——开启legacyFakeTimers: true后不能再同时设置advanceTimers、doNotFake、timerLimit等新式选项否则类型检查直接报错src/Config.ts 的联合类型设计保证了这一点。这也是类型化配置最直观的收益非法组合在写代码时就被拦截而不是等运行时报错。四、测试文件中的全局类型jest/globals 与 types/jest 两条路线README 的核心提醒是jest/types本身不提供describe/expect/it这些全局 API 的类型。如果你在测试文件里需要这些全局函数的类型提示有两条官方认可的路线4.1 路线一显式导入 jest/globals官方推荐import {describe, expect, it} from jest/globals; describe(my tests, () { it(works, () { expect(1).toBe(1); }); });jest/globals包对应仓库目录 packages/jest-globals把 Jest 的全局 API 以模块形式导出配合 TypeScript 使用能获得精确到参数与返回值的类型提示。这些 API 的签名定义正是来源于jest/types的Global命名空间——例如 src/Global.ts 中的TestFrameworkGlobals接口定义了it/test支持.only/.skip/.todo/.concurrent/.each/.failing、describe支持.only/.skip/.each、fit/xit/xtest以及四个 hookbeforeAll/beforeEach/afterEach/afterAll的完整签名。值得留意的是Each接口的重载设计src/Global.ts它覆盖了对象数组、元组数组、数组的数组、模板字符串四种each表格写法每种写法都能把表格行类型准确推导到测试回调的参数上。it.each([{a: 1}])(…, ({a}) …)中a被推导为number正是这套重载的功劳。4.2 路线二安装第三方 types/jest无需导入如果你希望不写 import 也能在测试文件中直接使用全局describe/expect/it可以安装types/jest。但 README 特别提醒了三点这是第三方包由 DefinitelyTyped 社区维护它可能无法覆盖最新的 Jest 特性例如较新的test.failing、test.concurrent变体、jest.replaceProperty等它的另一个用途才是补全jest.config.ts的配置类型即上一节提到的Config.InitialOptions用法见 packages/jest-types/README.md。两条路线的选择建议追求类型与最新 API 同步用jest/globals偏好全局无导入的旧式体验且对版本敏感度不高用types/jest。五、源码纵深其余四个命名空间的内部结构5.1 Global测试函数签名的类型基础除了 4.1 节提到的TestFrameworkGlobalsGlobal命名空间还定义了测试函数的“可返回类型”约束。从 src/Global.ts 可以看到TestReturnValue只允许void | undefined | Promiseunknown这解释了为什么 Jest 会警告“不要在测试函数里 return 非 Promise 的值”。TestFn是PromiseReturningTestFn | GeneratorReturningTestFn | DoneTakingTestFn的联合src/Global.ts统一了异步函数、生成器函数与基于done回调三种测试风格。Global接口src/Global.ts则在globalThis的基础上叠加了测试框架全局与__coverage__字段构成了测试环境中全局对象的类型画像。5.2 TestResult测试产物的数据结构AssertionResultsrc/TestResult.ts描述单条断言结果的完整信息ancestorTitlesdescribe 层级、fullName、statuspassed | failed | skipped | pending | todo | disabled | focused见 src/TestResult.ts、failureMessages、failureDetails、numPassingAsserts等。文件注释还解释了跨 worker 序列化的现实约束function或symbol类型的原始值可能丢失相关信息会以字符串形式出现在failureMessages中。--collectTests模式下列为“被发现但未执行”的测试会带有wouldRun标记src/TestResult.ts。这些类型是报告器reporter、--json输出与各类结果处理插件的数据契约。5.3 Circus默认测试运行器的事件协议Jest 默认运行器 Circus 本质上是一个“事件驱动”的测试执行引擎Circus命名空间给出了完整的事件协议同步事件SyncEventstart_describe_definition、add_hook、add_test、error等见 src/Circus.ts与异步事件AsyncEventsetup、hook_start、test_fn_success、test_retry、test_done、run_finish、teardown等见 src/Circus.ts两两分类State记录运行期全部可变状态当前 describe 块、seed、testTimeout、未处理 rejection 的追踪 Map 等见 src/Circus.tsTestEntry与DescribeBlock则是测试与 describe 块在运行期的内部表示src/Circus.ts。如果你在编写自定义事件处理器或研究test.retry、describe.retry、test.failing的实现这套类型就是最好的“事件字典”。5.4 TransformTypes代码转换器的类型契约CallerTransformOptionssupportsDynamicImport、supportsExportNamespaceFrom、supportsStaticESM、supportsTopLevelAwait见 src/Transform.ts与 Babel 的 caller 选项字段一一对应CacheKeyOptions在转换选项基础上追加了config与configString供自定义 transformer 生成缓存键时做缓存失效判断src/Transform.tsTransformResult则规范了转换产物必须携带code、originalCode与可选的sourceMapPathsrc/Transform.ts。编写自定义 transformer 时实现TransformResult的结构是返回值的硬性要求。六、jest/types 与 jest/schemas 的分工细心的读者会注意到配置类型InitialOptions实际由jest/schemas定义src/Config.ts那么两个包的分工是什么从仓库结构可以推断jest/schemas负责面向用户输入的配置 schemaInitialOptions、SnapshotFormat等强调“用户写了什么”jest/types则在更广的层面聚合面向内部执行链路的全部共享类型ProjectConfig、GlobalConfig、Argv、TestResult、Circus事件等。二者是“外部输入”与“内部契约”的关系共同保障了配置从jest.config.ts到各执行包之间类型不失真。七、最佳实践小结配置类型化jest.config.ts中使用import type {Config} from jest/types并标注Config.InitialOptions可获得配置项的完整校验与补全全局 API 类型测试文件中优先从jest/globals显式导入describe/expect/it以获得与最新 Jest 同步的类型若坚持无导入的全局体验再考虑第三方types/jest并留意其可能的版本滞后类型即文档遇到不确定的配置选项如fakeTimers的互斥规则、coverageThreshold的路径结构、projects的字符串/对象混合写法直接查阅 packages/jest-types/src/Config.ts 与 packages/jest-types/typetests/config.test.ts 中的类型定义与类型测试比翻文档更快、更准关注版本本仓库中jest/types版本为 30.4.1见 packages/jest-types/package.jsonProjectConfig中部分字段如workerThreads在源码注释中标注了后续版本的非可选化计划src/Config.ts升级 Jest 时留意类型声明随版本的演进即可。总而言之jest/types是理解 Jest 类型体系的最佳切入点向上承接用户配置与全局 API向下贯穿运行器事件、测试结果与代码转换是 Jest 这座大型 TypeScript 项目中名副其实的“类型中枢”。【免费下载链接】jestDelightful JavaScript Testing.项目地址: https://gitcode.com/gh_mirrors/je/jest创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考