Effect 库 Result.failVoid 深入解析:零载荷失败信号的构造与实战应用
Effect 库 Result.failVoid 深入解析零载荷失败信号的构造与实战应用【免费下载链接】effectBuild production-ready applications in TypeScript项目地址: https://gitcode.com/GitHub_Trending/ef/effect导读本文围绕 Effect 开源仓库中 .changeset/pre/dirty-lamps-trade.md 所记录的Result.failVoid新增变更展开系统讲解这一 API 的设计动机、源码实现、与Result.void的对称关系以及它在filterMap、findFirstFilter等组合子中的典型用法。读完本文你将掌握如何在 Effect 的Result数据模型下以零开销、零载荷的方式表达仅表示失败、不携带错误数据的控制信号。变更背景一条 changeset 背后的新 APIdirty-lamps-trade.md是一份典型的 Changesets 变更记录内容为--- effect: patch --- add Result.failVoid这条记录声明对effect包施加一个patch补丁级变更变更内容即新增Result.failVoid导出。尽管条目极简它对应的是 Effect 4.x 中Result模块的一处真实 API 扩展其设计、实现与测试都能在仓库源码中得到完整印证。本文即以此为锚点深入剖析该 API 的全部技术细节。Result 数据模型纯数据化的成功/失败表示在理解failVoid之前需要先明确Result在 Effect 中的定位。根据 packages/effect/src/Result.ts 的模块级文档ResultA, E是一个已经成功或已经失败的值其定义为联合类型SuccessA, E | FailureA, E见 Result.ts当值可用时它是SuccessA, E当错误可用时它是FailureA, E它是纯数据检查或变换它不会触发副作用E默认是never因此Resultnumber表示一个不可能失败的结果模块提供构造、检查、映射、组合、提取以及与Option、可空值互转等辅助函数。Result与Option的关键差异在于Option会丢失失败信息而Result同时保留成功值与失败值两条路径的数据。当业务上成功与失败都应当作为数据保留时应优先使用Result。构造器家族succeed 与 failResult的两个基础构造器位于 packages/effect/src/Result.tsResult.fail: E(left: E) Resultnever, E以给定值构造失败结果Result.succeed: A(success: A) ResultA以给定值构造成功结果。其底层实现在 packages/effect/src/internal/result.tsfail返回new FailureImpl(failure)succeed返回new SuccessImpl(success)即每次调用都会分配一个新的包装对象。例如基础用法import { Result } from effect const ok: Result.Resultnumber Result.succeed(42) const err: Result.Resultnever, string Result.fail(Network error) // 用 match 折叠两个分支 Result.match(ok, { onSuccess: (value) Success: ${value}, onFailure: (error) Error: ${error} }) // Success: 42Result.failVoid零载荷失败信号的预构建常量定义与类型新增的Result.failVoid定义于 packages/effect/src/Result.tsexport const failVoid: Resultnever, void fail(void 0)它的类型为Resultnever, void——成功分支是never不可能成功失败分支的错误载荷是void即undefined。文档注释明确指出适用场景需要一个仅充当控制信号、不携带失败数据的失败结果等价性它等价于Result.fail(undefined)但复用同一个共享的Failure包装而不是每次分配新对象引入版本since 4.0.0。也就是说failVoid与普通的Result.fail相比多了一层单例复用的优化语义模块加载时即完成一次构造之后所有引用共享同一实例避免高频路径下的重复分配。与 Result.void 的对称设计failVoid并非孤例它与早先引入的Result.voidsince 3.13.0见 Result.ts构成完整对称API类型语义等价写法Result.voidResultvoid成功的空信号共享Success包装Result.succeed(undefined)Result.failVoidResultnever, void失败的空信号共享Failure包装Result.fail(undefined)两者的 JSDoc 措辞完全对应一个说成功完成但无需携带有意义数据一个说失败但仅作控制信号、无失败数据。这种成对设计让调用方在只需要一个布尔式的成败判定、不需要具体载荷的场景中可以写出意图明确且零分配的代码。实战用法filterMap 系列组合子中的失败哨兵Result.failVoid最常见的用途是作为filterMap类组合子的过滤哨兵回调返回Result.succeed(v)表示保留该元素返回Result.failVoid表示丢弃该元素。仓库测试中大量采用这一模式可直接作为可运行的参考。Chunk.filterMap 与 filterMapWhile见 packages/effect/test/Chunk.test.tsimport { Chunk, Result } from effect // 保留偶数并翻倍 Chunk.filterMap(Chunk.make(1, 2, 3, 4), (n) n % 2 0 ? Result.succeed(n * 2) : Result.failVoid) // 带索引版本 Chunk.filterMap(Chunk.make(1, 2, 3, 4), (n, i) i % 2 0 ? Result.succeed(n i) : Result.failVoid) // filterMapWhile命中失败哨兵即终止 Chunk.filterMapWhile(Chunk.make(1, 3, 4, 5), (n) n % 2 1 ? Result.succeed(n) : Result.failVoid)HashMap.filterMap 与 Record.filterMap同样的模式扩展到了键值容器见 packages/effect/test/HashMap.test.ts 与 packages/effect/test/Record.test.tsimport { HashMap, Record, Result } from effect HashMap.filterMap(map1, (value) value % 2 0 ? Result.succeed(value * 2) : Result.failVoid) Record.filterMap(x, (value, key) (value 2 ? Result.succeed(key) : Result.failVoid))Effect.findFirstFilter效应化场景Result.failVoid还能作为效应管线中的哨兵见 packages/effect/test/Effect.test.tsimport { Effect, Result } from effect // 过滤并映射 Effect.succeed(n % 2 0 ? Result.succeed(n${n}) : Result.failVoid) // 查找第一个满足条件的元素 const result yield* Effect.findFirstFilter([1, 2, 3], () Effect.succeed(Result.failVoid))此外Option.test.ts、Iterable.test.ts、Sink.test.ts 等测试文件也使用了同一模式说明failVoid已作为filterMap/filterMapWhile语义的标准失败哨兵被广泛采用。判别与折叠isFailure 与 match 的配合当消费方拿到一个Result可能正是failVoid时可用类型守卫与折叠函数处理Result.isFailure将ResultA, E收窄为FailureA, E从而通过.failure属性读取错误载荷Result.match同时为成功/失败两个分支提供折叠逻辑见 Result.ts 的示例。import { Result } from effect const signal: Result.Resultnever, void Result.failVoid Result.match(signal, { onSuccess: () unreachable, onFailure: (error) failed with: ${String(error)} })由于failVoid的载荷是void实践中通常只关心分支本身错误值undefined仅充当存在性标记。小结何时应该使用 Result.failVoid综合源码注释与测试用例可以归纳出以下选择依据只需要成败判定、不需要错误信息时用Result.failVoid替代Result.fail(...)避免为无意义载荷引入字符串字面量高频调用路径如对大型集合执行filterMap中共享实例可省去每次回调的包装对象分配配合filterMap/filterMapWhile/findFirstFilter等组合子作为过滤哨兵使意图比null或布尔值更明确若错误侧需要携带数据则应使用Result.fail(error)若成功侧需要携带数据则使用Result.succeed(value)成功侧的空信号则由Result.void负责。该 API 自 Effect 4.0.0 起可用见 Result.ts在 Result.test.ts 中已通过deepStrictEqual(Result.void, Result.succeed(undefined))一类的相等性断言保障其语义稳定开发者可以放心在 4.x 版本中直接使用。【免费下载链接】effectBuild production-ready applications in TypeScript项目地址: https://gitcode.com/GitHub_Trending/ef/effect创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考