ARTICLE DETAIL

资讯详情

深耕网站视觉设计与运营推广的一线实战洞察。

t3code 仓库中 effect-smol 的 Schema.FilterOutput 扩展:自定义过滤器失败上报机制的完整解析

t3code 仓库中 effect-smol 的 Schema.FilterOutput 扩展:自定义过滤器失败上报机制的完整解析 t3code 仓库中 effect-smol 的 Schema.FilterOutput 扩展:自定义过滤器失败上报机制的完整解析【免费下载链接】t3code项目地址: https://gitcode.com/GitHub_Trending/t3/t3code本文以 t3code 仓库内 vendored 的参考仓库.repos/effect-smol中的 changeset 文档为骨架,完整解析 effect 包 Schema 模块中Schema.makeFilter谓词返回类型FilterOutput的扩展与新增类型Schema.FilterIssue。读完本文,你将掌握过滤器失败上报的全部合法返回形状、{ path, issue }与旧{ path, message }的破坏性迁移方式,以及多字段校验结果如何被归一化为Issue.Composite的源码级实现。变更来源与在 t3code 仓库中的位置t3code 主仓库通过.repos/目录内置了若干参考仓库,其中.repos/effect-smol是 effect 生态的精简版 monorepo,包含effect、schema相关的核心实现包。本次主题对应的变更说明文件位于:.repos/effect-smol/.changeset/pre/expand-schema-filter-output.md该 changeset 的 frontmatter 标注effect: patch,即对effect包进行补丁级版本递增。其核心声明如下:Schema: expandFilterOutputand addFilterIssuefor richer filter failures.也就是说,这是一次围绕「自定义校验过滤器如何向 Schema 解码失败系统上报错误」的 API 扩展:谓词函数可以返回的取值空间变大了,同时旧的{ path, message }对象形状被破坏性地重命名为{ path, issue }。FilterOutput 的完整取值空间在 effect-smol 的源码中,FilterOutput与FilterIssue定义在effect包的 Schema 模块里,见 Schema.ts:/** 单个失败项,可单独返回,也是数组分支的元素类型 */ export type FilterIssue string | SchemaIssue.Issue | { readonly path: ReadonlyArrayPropertyKey readonly issue: string | SchemaIssue.Issue } /** makeFilter 谓词函数可返回的全部形状 */ export type FilterOutput | undefined | boolean | FilterIssue | ReadonlyArrayFilterIssue谓词函数本身由 makeFilter 构造:export const makeFilter: T( filter: (input: T, ast: SchemaAST.AST, options: SchemaAST.ParseOptions) FilterOutput, annotations?: Annotations.Filter | undefined, abort?: boolean ) SchemaAST.FilterT SchemaAST.makeFilter谓词接收已解码的输入值、当前 Schema AST和解析选项三参,返回值必须落在FilterOutput之中。各形状的语义(结合 Schema.ts 中的文档注释)可归纳为:返回形状语义undefined/true校验通过false通用失败,无自定义消息string以该字符串作为失败消息SchemaIssue.Issue直接返回一个完整构造好的 Issue{ path, issue }在嵌套路径处挂载失败(本次扩展后issue支持string \| SchemaIssue.Issue)ReadonlyArraySchema.FilterIssue一次性上报多个失败(本次新增的数组分支)其中「单失败形状(undefined、true、false、string、SchemaIssue.Issue)保持不变」这一点是 changeset 中明确声明的兼容性边界:所有旧代码若只使用这些形状,无需任何修改。新增形状一:{ path, issue }支持挂载完整 Issuechangeset 指出,{ path, issue }这一分支中issue的取值从「仅字符串」扩展为string | SchemaIssue.Issue。源码文档注释(Schema.ts)给出了两种取值的归一化差异:issue为string:会被包装进一个SchemaIssue.InvalidValue,且该包装遵循reportInput选项(即开启reportInput时会在错误中附带输入值);issue为SchemaIssue.Issue:原样返回,不做reportInput增强;无论哪种,最终结果都会再被包进一个位于给定path的SchemaIssue.Pointer。这个扩展的实际价值在于:以前想在嵌套字段路径上挂一个结构化的 Issue(例如带expected标注的InvalidValue),需要手动构造Pointer逐层包裹;现在直接在{ path, issue }里传入完整 Issue 即可。makeFilter文档注释中的官方示例(Schema.ts)展示了「在嵌套路径上报失败」的用法:import { Result, Schema } from effect const schema Schema.Struct({ password: Schema.String, confirmPassword: Schema.String }).check( Schema.makeFilter((o) o.password o.confirmPassword ? undefined : { path: [password], issue: password and confirmPassword must match } ) ) const result Schema.decodeUnknownResult(schema)({ password: 123456, confirmPassword: 1234567 }) if (Result.isFailure(result) result.failure.issue._tag Filter result.failure.issue.issue._tag Pointer) { result.failure.issue.issue.path // [password] }解码失败时,错误树呈现为Filter包装Pointer,Pointer.path即谓词中指定的嵌套路径,这是校验结果可定位到具体字段的基础。新增形状二:ReadonlyArrayFilterIssue批量上报changeset 对数组分支的三条规则做了精确约定,与源码实现逐条对应:空数组 校验通过;单元素数组 等价于直接返回该元素;多元素数组 聚合为一个Issue.Composite。官方示例(Schema.ts)演示了跨字段的条件校验(「若a 0则要求b、c同时大于 0」),并一次性收集所有违规:import { Result, Schema } from effect const schema Schema.Struct({ a: Schema.Finite, b: Schema.Finite, c: Schema.Finite }).check( Schema.makeFilter((o) { const issues: ArraySchema.FilterIssue [] if (o.a 0) { if (o.b 0) issues.push({ path: [b], issue: b must be greater than 0 }) if (o.c 0) issues.push({ path: [c], issue: c must be greater than 0 }) } return issues }) ) const result Schema.decodeUnknownResult(schema)({ a: 1, b: 0, c: 0 }) if (Result.isFailure(result) result.failure.issue._tag Filter result.failure.issue.issue._tag Composite) { result.failure.issue.issue.issues.map((issue) issue._tag Pointer ? issue.path : []) // [[b], [c]] }changeset 强调的动机是:此前实现多字段校验器需要显式导入SchemaIssue、手工new Composite(...);数组分支把这些样板代码吸收进了归一化逻辑,谓词只需要「push 失败项、return 数组」。源码级实现:normalizeFilterOutput 的归一化路径makeFilter只是薄封装,真正的工作发生在 AST 层。SchemaAST.makeFilter 把谓词输出直接委托给SchemaIssue.normalizeFilterOutput:export function makeFilterT( filter: (input: T, ast: AST, options: ParseOptions) Schema.FilterOutput, annotations?: Schema.Annotations.Filter | undefined, aborted: boolean false ): FilterT { return new Filter( (input, ast, options) SchemaIssue.normalizeFilterOutput(ast, filter(input, ast, options), input, options), annotations, aborted ) }归一化核心实现见 SchemaIssue.normalizeFilterOutput,其分支结构与 changeset 的三条规则一一对应:export function normalizeFilterOutput( ast: SchemaAST.AST, out: Schema.FilterOutput, input?: unknown, options?: SchemaAST.ParseOptions ): Issue | undefined { if (Array.isArray(out)) { if (!Arr.isReadonlyArrayNonEmpty(out)) { return undefined // 空数组 成功 } return out.length 1 ? makeFilterIssue(out[0], input, options) // 单元素 展开 : new Composite(ast, Arr.map(out, (entry) makeFilterIssue(entry, input, options)), input, options) } return makeSingle(out as undefined | boolean | Schema.FilterIssue, input, options) }从源码结构看,数组分支先判空(非空数组才进入失败路径),长度为一时走单元素归一化,否则逐项归一化后构造Composite。单值分支则由makeSingle处理undefined/boolean/FilterIssue三种形状,{ path, issue }在此阶段被展开为Pointer包装——这与前文「挂载嵌套路径失败」示例中错误树出现_tag Pointer完全吻合。另外两个与makeFilter行为相关的细节同样来自文档注释(Schema.ts):annotations参数作用于过滤器本身;默认格式化器对失败的取用顺序是message优先、expected次之,两者都缺省时显示filter;abort为true时,该过滤器失败后解析立即停止,不再收集后续 check 的失败。破坏性重命名:{ path, message }→{ path, issue }changeset 明确标注Breaking的部分是对象形状的字段重命名,迁移是机械式的:// before Schema.makeFilter((o) ({ path: [a], message: bad })) // after Schema.makeFilter((o) ({ path: [a], issue: bad }))需要改动的是所有使用旧对象形状的调用点:把返回对象里的message键改名为issue,若原本就是字符串消息,值本身可以原样保留。changeset 末尾还指出,同一重命名同步应用到了SchemaGetter.checkEffect的合法返回类型上。源码中该约束体现在 SchemaGetter.ts:export function checkEffectT, R never( f: (input: T, options: SchemaAST.ParseOptions) Effect.Effect undefined | boolean | Schema.FilterIssue, never, R ): GetterT, T, R {可以看到checkEffect的 Effect 成功通道直接以Schema.FilterIssue为元素类型(外加undefined | boolean),即异步 getter 校验复用了同一套失败项定义,{ path, message }旧形状在两条 API 路径上必须一致迁移。迁移与验证清单结合 changeset 与源码,升级到新 API 时可以按以下清单操作:全局搜索返回对象中的message:键(限定在makeFilter谓词与SchemaGetter.checkEffect上下文),将其改为issue:;纯字符串/布尔返回值的谓词无需改动;若旧代码曾手工导入SchemaIssue并new Composite(...)聚合多字段失败,可以改写为ReadonlyArraySchema.FilterIssue数组返回,删除手工聚合逻辑;验证时可用Schema.decodeUnknownResult检查失败 Issue 的_tag是否为预期的Filter→Pointer(单路径失败)或Filter→Composite(数组多失败),与上文官方示例的断言方式一致;注意{ path, issue }中直接传SchemaIssue.Issue时不再享受reportInput增强——若依赖错误输出携带输入值,应保持issue为字符串或自行构造含输入信息的 Issue。参考文件变更说明:.repos/effect-smol/.changeset/pre/expand-schema-filter-output.mdFilterIssue/FilterOutput类型与makeFilter:.repos/effect-smol/packages/effect/src/Schema.tsAST 层过滤器构造:.repos/effect-smol/packages/effect/src/SchemaAST.ts输出归一化实现:.repos/effect-smol/packages/effect/src/SchemaIssue.ts异步 getter 校验返回类型:.repos/effect-smol/packages/effect/src/SchemaGetter.ts【免费下载链接】t3code项目地址: https://gitcode.com/GitHub_Trending/t3/t3code创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
返回列表