- 开发工具
- 测试
【免费下载链接】fast-check
Property based testing framework for JavaScript (like QuickCheck) written in TypeScript
本文基于 fast-check 官方发布博客(website/blog/2024-03-04-whats-new-in-fast-check-3-16-0/index.md)展开,结合当前仓库源码逐项验证 3.16.0 的三个核心变化:
fc.pre正式成为 TypeScript 断言函数(assertion function)、monorepo 全面向 ECMAScript Modules 迁移、以及官网性能接近 PageSpeed 满分。读完本文,你将理解fc.pre的类型收窄原理与底层执行机制,掌握 ESM 双格式发布的现状,并了解一套可复用的静态文档站点性能优化清单。
一、fc.pre成为断言函数:让类型在测试中自动收窄
1.1 版本发布前的问题:pre 之后仍需手动断言
fc.pre是 fast-check 中声明前置条件(precondition)的核心 API,用于在属性测试中丢弃不符合前提的生成值。在 3.16.0 之前,fc.pre的返回类型无法向 TypeScript 表达"调用成功后条件必然成立"这一信息,因此开发者不得不额外写出类型断言:
// entries: {value: T} const match = entries.find((e) => e.value === needle); fc.pre(match !== undefined); // ❌ Previous (<3.16.0): match: {value: T} | undefined // ✅ Now (≥3.16.0): match: {value: T}升级到 3.16.0 之后,fc.pre(match !== undefined)一行即可让编译器在后续代码中将match收窄为{value: T},不再需要手写as或!。
1.2 源码级验证:asserts签名与失败语义
在 packages/fast-check/src/check/precondition/Pre.ts 中,pre的实现只有几行:
export function pre(expectTruthy: boolean): asserts expectTruthy { if (!expectTruthy) { throw new PreconditionFailure(); } }关键点有两个:
asserts expectTruthy签名:这是 TypeScript 3.7 引入的断言函数语法,它向编译器声明"若此函数正常返回,则传入的表达式必然为真",从而触发后续代码的类型收窄(narrowing)。- 失败即抛异常:当条件为假时抛出
PreconditionFailure。该异常类定义在 packages/fast-check/src/check/precondition/PreconditionFailure.ts,内部通过Symbol.for('fast-check/PreconditionFailure')作为全局共享足迹(footprint),isFailure(err)静态方法据此识别"由 fast-check 抛出的前置条件失败",即使跨包实例(如fc.pre(false)与其他实例互认)也能正确判定。
1.3 底层执行机制:前置条件失败为何不算测试失败
PreconditionFailure被抛出后,由属性执行层捕获并特殊处理。在 packages/fast-check/src/check/property/_internals/PropertyImplem.ts 中:
function errorToPropertyAnswer(err: unknown) { // precondition failure considered as success for the first version if (PreconditionFailure.isFailure(err)) return err; // exception as PropertyFailure in case of real failure return { error: err }; }也就是说,fc.pre抛出的是"本轮生成值被丢弃"的信号,会被转换为PreconditionFailure返回值,而不是PropertyFailure——这正是属性测试中"such that precondition holds"语义的落地。运行器在 RunnerIterator.ts 中同样只把非PreconditionFailure的结果当作真实失败处理。
对应的单元测试位于 packages/fast-check/src/check/precondition/Pre.spec.ts,覆盖了三个行为:真值不抛异常、假值抛出可识别的PreconditionFailure、以及跨 fast-check 实例的异常互认。
1.4 实战用法与取舍
在 website/docs/core-blocks/properties.md 中,官方文档将属性测试公式拆解为:"for any (x, y, ...)" 由 arbitraries 负责,"such that precondition(x, y, ...) holds" 由fc.pre或.filter负责,"predicate(x, y, ...) is true" 由断言负责。典型写法如下:
fc.property(fc.nat(), fc.string(), (maxLength, label) => { fc.pre(label.length <= maxLength); // 任何 label.length > maxLength 的生成值都会被丢弃 return crop(label, maxLength) === label; });仓库示例中也大量使用该 API,例如 examples/003-misc/roman/main.spec.ts 中的fc.pre(n1 !== n2),以及 examples/002-recursive/isSearchTree/main.spec.ts 中对非排序遍历的前置过滤。
需要留意的是,官方文档同时强调:fc.pre与.filter本质上都是"先生成、后丢弃",过滤条件过严会显著浪费生成量。因此只要 arbitrary 本身支持约束(如fc.string({ minLength: 2 })),应优先使用生成器参数而非过滤手段。
二、迈向 ECMAScript Modules:monorepo 双格式发布路线
2.1 为什么迁移:ESM 是模块标准的正统继承者
ECMAScript Modules 是 CommonJS(Node.js)以及 Require/AMD/UMD(浏览器端)的官方继任者,目标是为不同运行时提供统一的import方式。fast-check 起步时 ESM 在 Node.js 中几乎不可用,直到 12.17.0 才出现第一个不带 flag 的版本;彼时生态尚未就绪,因此项目长期停留在 CommonJS。
2.2 迁移进度:先包后主,逐步推进
3.16.0 发布时,monorepo 的迁移策略是在各包package.json中逐个加入"type": "module",当时除/packages/fast-check(进行中)与/website(Docusaurus 暂不支持)外,其余包均已迁移完成。从当前仓库验证:
- 根目录 package.json 与各子包均已声明
"type": "module":- packages/ava/package.json
- packages/jest/package.json
- packages/vitest/package.json
- packages/worker/package.json
- packages/poisoning/package.json
- packages/packaged/package.json
- packages/fast-check/package.json 当前版本为 4.10.2,同样已是
"type": "module",说明博客中标注为"进行中"的主包迁移后续已随 4.x 大版本完成,其exports字段指向./lib/fast-check.js。
2.3 对用户的影响:双版本仍会同时发布
博客明确承诺:迁移对用户"无可见影响",因为官方仍计划同时发布 ESM 与 CommonJS 两种产物。这意味着无论使用方的模块体系是 CJS 还是 ESM,都可以继续通过既有方式引入 fast-check;迁移更多是构建链(rolldown)与仓库内部工具链的现代化,而非破坏性的 API 变更。配套的 CI 改动(如 PR#4591 将 monorepo 根构建链迁移到 ESM、PR#4650 将 examples 构建链迁移到 ESM)也印证了"自下而上、逐层迁移"的推进方式。
三、更高效的文档:一套可复用的站点性能优化清单
3.1 背景:以 PageSpeed 为基准做性能评估
官方文档站(fast-check.dev)是用户了解 API 的关键资源。2024 年 2 月初,团队开始用 PageSpeed(Lighthouse 在线版)评估首页性能,先处理了一批"低垂果实",随后发现部分优化必须更换托管基础设施,于是迁移到能更精细控制响应头(cache-control、content-security-policy)的 Netlify。截至 3.16.0 发布,移动端评分已接近 100/100。
3.2 六项关键优化及其原理
- 消除图片引起的布局偏移(layout shift):图片出现时若没有预留空间会导致页面跳动。做法是在 DOM 中直接为图片声明
height与width,从根源上减少 CLS 指标。 - 更合理的缓存策略:静态资源必须被缓存。此前 GitHub Pages 对静态资源仅缓存几分钟,缓存形同虚设;切换托管方案后缓存头得以正确配置。
- 移除 Google Analytics:实测该脚本占据了约 50% 的 JavaScript bundle 体积与 50% 的主线程阻塞时间,属于性价比极低的性能负担,因而被移除(对应 PR#4776)。
- 按真实比例提供图片:例如 16/9 的图片不应以 16/10 的尺寸渲染,避免拉伸与多余的解码开销。
- 按实际展示尺寸提供图片:头像只需 64×64 就绝不下发更大文件,做到"既不更大也不更小"。
- 优化 SVG 资源:SVG 可去除不影响观感的冗余细节(元数据、多余路径等),从而减小体积(对应 PR#4732 "Optimize SVG assets")。
3.3 安全与质量配套:CSP 与 CI 校验
性能之外,同批改动还包含安全加固与 CI 质量门禁:
- Content-Security-Policy:为 fast-check.dev 配置并强制启用 CSP(PR#4753、PR#4761),随后为兼容 Algolia 搜索放宽策略(PR#4772)。
- CI 校验:新增 Docusaurus 的
onBrokenAnchors检查(PR#4598),让失效锚点直接导致构建失败;部署迁移到 Netlify(PR#4747)并移除 GitHub Pages 配置(PR#4751),同时让 CI 在无效部署时失败(PR#4756)。
这些实践对任何以文档站为核心的开发者项目都有直接参考价值:先量化(PageSpeed),再分级(低垂果实 → 基础设施),最后用 CI 门禁守住质量。
四、3.16.0 更新日志(自 3.15.0 起)
3.16.0 基于 3.15.1,但相比 3.15.0 的主要变更如下:
Features
- 让
fc.pre成为断言函数(PR#4709)——即本文第一节所述的类型收窄能力。
Fixes
- Bug:小屏幕下 Logo 比例错误(PR#4736)。
- CI:monorepo 根构建链迁移到 ESM(PR#4591);Docusaurus 增加
onBrokenAnchors检查(PR#4598);补充 VSCode 配置文件(PR#4606);examples 构建链迁移到 ESM(PR#4650);官网部署迁移到 Netlify(PR#4747);移除 GitHub Pages 配置(PR#4751);CI 对无效部署报错(PR#4756);移除 Google Analytics(PR#4776)。 - Clean:移除针对 React 17 的遗留补丁(PR#4769)。
- Doc:在 website/docs/ecosystem.md 中新增
idonttrustlikethat-fast-check与@effect/schema;在 track record 中新增jsonwebtoken;新增贡献者 nielk、ej-shafran、gruhn;修复官网控制台报错;优化首页图片与 SVG 资产、消除布局偏移;为 CTA 增加顶部留白;发布 3.15.0 博客(PR#4554、PR#4563、PR#4669、PR#4677、PR#4712、PR#4713、PR#4730、PR#4732、PR#4735、PR#4750、PR#4754、PR#4768、PR#4771)。 - Security:为 fast-check.dev 配置并强制 CSP,随后为 Algolia 放宽策略(PR#4753、PR#4761、PR#4772)。
- Test:修复 double 的
isCorrect检查(PR#4665);稳定 URL 相关的不稳定测试(PR#4666)。
五、小结
fast-check 3.16.0 是一次"体验与基建并重"的版本:对使用者最直观的是fc.pre获得断言函数签名,让前置条件过滤的同时自动完成类型收窄,且其"抛出PreconditionFailure→ 被属性执行层识别为丢弃而非失败"的机制在 PropertyImplem.ts 中清晰可见;对维护者而言,ESM 迁移与文档站性能优化则为 4.x 大版本铺平了道路。从当前仓库看,这些计划均已落地:主包 package.json 已切换为"type": "module",各子包亦然,双格式发布策略得以延续。如需深入阅读,可继续查看 properties 文档 与 Pre 的单元测试。
- 开发工具
- 测试
【免费下载链接】fast-check
Property based testing framework for JavaScript (like QuickCheck) written in TypeScript
相关推荐
Nuxt.js 3.16.0 发布:性能优化与新特性解析
Nuxt.js 3.16.0 发布:性能优化与新特性解析 你是否还在为Nuxt应用的加载速度和用户体验烦恼?Nuxt.js 3.16.0版本带来了一系列性能优化
前端后端Web框架SSRRemeda hasProp 属性存在性检查:与 Just `has` 的迁移对照与类型收窄原理
Remeda hasProp 属性存在性检查:与 Just has 的迁移对照与类型收窄原理 导读 本文围绕 Remeda 的 hasProp 工具函数展开,讲
开发工具Slate v2 React 运行时收窄批次:大型文档输入性能优化实战解析
Slate v2 React 运行时收窄批次:大型文档输入性能优化实战解析 导读 本文围绕 plate 仓库中 Slate v2 重写项目的一个关键性能优化批次
前端富文本UI组件
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考