十年匠心定制 · 商业建站与技术教学双线并行 咨询热线:400-886-1026 service@lmnt.cn
ARTICLE DETAIL

资讯详情

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

Effect Schema 的 OptionFromUndefinedOr 与 OptionFromNullishOr:用 Option 统一处理 undefined/null 可选字段

Effect Schema 的 OptionFromUndefinedOr 与 OptionFromNullishOr:用 Option 统一处理 undefined/null 可选字段 Effect Schema 的 OptionFromUndefinedOr 与 OptionFromNullishOr用 Option 统一处理 undefined/null 可选字段【免费下载链接】t3code项目地址: https://gitcode.com/GitHub_Trending/t3/t3code导读在对接第三方 API 或数据库数据时undefined、null乃至缺失字段往往都表示没有值直接处理会让业务代码充斥着判空分支。effect 仓库当前版本4.0.0-rc.112通过 changeset 新增了Schema.OptionFromUndefinedOr与Schema.OptionFromNullishOr两个 schema 构造器把可为 undefined / 可为 nullish 的值编码为 Option 语义让解码、编码与校验在一个 schema 内闭环完成。阅读完本文你将掌握这两个构造器的行为边界、onNoneEncoding配置的编码差异以及它们与既有OptionFromNullOr、OptionFromOptional系列 API 的取舍。本文依据变更记录 add-schema-option-from-undefined-nullish.md 展开所有结论均可追溯至 Schema.ts 源码与 Schema.test.ts 测试用例。变更背景一条 changeset 引出的 API 家族该 changeset 文件位于 .changeset/pre/内容为--- effect: patch --- Schema: add OptionFromUndefinedOr and OptionFromNullishOr schemas.它标记为patch级别意味着这是向后兼容的新增能力。与之对应的 CHANGELOG 条目记录在 packages/effect/CHANGELOG.md归属于4.0.0-beta.16版本由gcanti贡献PR #1508。需要说明的是该 changeset 当前位于pre目录处于pre发布模式因此这条变更以patch形式进入下一个 beta 版本而不是立即出现在当前已发布的4.0.0-rc.112中。从源码注释的since 3.10.0 / 4.0.0标记可以推断OptionFromNullOr3.10.0 引入是更早的成员而本文主角OptionFromUndefinedOr、OptionFromNullishOr与OptionFromOptionalKey、OptionFromOptional均标为since 4.0.0属于同一批面向 v4 的规范化设计。两个新构造器的行为契约OptionFromUndefinedOr只把 undefined 视为无值源码定义位于 Schema.tsexport function OptionFromUndefinedOrS extends Constraint(schema: S): OptionFromUndefinedOrS { return UndefinedOr(schema).pipe(decodeTo( Option(toType(schema)), SchemaTransformation.optionFromUndefinedOr() )) }从源码结构看它由三部分组合而成UndefinedOr(schema)底层是S | undefined的联合 schema见 Schema.ts#L5020-L5035实现为Union([self, Undefined])Option(toType(schema))目标类型为OptionS 的解码类型SchemaTransformation.optionFromUndefinedOr()注入转换逻辑。核心语义解码方向输入值解码结果undefinedOption.none()其他任意值包括nullOption.some(value)核心语义编码方向Option.none()编回undefinedOption.some(v)编回v。转换逻辑的底层实现在 SchemaTransformation.ts#L1230-L1235export function optionFromUndefinedOrT(): TransformationOption.OptionT, T | undefined { return transform({ decode: Option.fromUndefinedOr, encode: Option.getOrUndefined }) }它直接复用了 Option.ts#L807-L809 的Option.fromUndefinedOra undefined ? none() : some(a)与Option.getOrUndefinedgetOrElse(constUndefined)。也就是说解码时只有undefined会被折叠为Nonenull会被保留在Some里——这一点对数据库里 null 有意义、undefined 才表示缺省的字段至关重要。OptionFromNullishOrnull 与 undefined 都视为无值源码定义位于 Schema.ts#L9850-L9860export function OptionFromNullishOrS extends Constraint( schema: S, options?: { onNoneEncoding: null | undefined } ): OptionFromNullishOrS { return NullishOr(schema).pipe(decodeTo( Option(toType(schema)), SchemaTransformation.optionFromNullishOr(options) )) }与上一构造器的差异点有二底层联合 schema 是NullishOr(schema)即S | null | undefined见 Schema.ts#L5043-L5058实现为Union([self, Null, Undefined])新增options.onNoneEncoding参数控制None编码回哪种空值。核心语义解码方向null和undefined都映射为Option.none()其余值映射为Option.some(value)。核心语义编码方向Option状态onNoneEncoding编码结果none()undefined默认undefinednone()nullnullsome(v)任意v底层实现在 SchemaTransformation.ts#L1274-L1283export function optionFromNullishOrT( options?: { onNoneEncoding: null | undefined } ): TransformationOption.OptionT, T | null | undefined { return transform({ decode: Option.fromNullishOr, encode: options?.onNoneEncoding null ? Option.getOrNull : Option.getOrUndefined }) }onNoneEncoding的默认值是undefined只有显式传入null时None才会编码为null对应 Option.ts#L773-L775 的Option.fromNullishOr使用a null宽松判空同时覆盖null与undefined。组合后的类型签名与可推导性两个构造器都返回一个扩展了decodeToOptiontoTypeS, …的接口并带有Rebuild自引用字段OptionFromUndefinedOrSdecodeToOptiontoTypeS, UndefinedOrSSchema.ts#L9804-L9806OptionFromNullishOrSdecodeToOptiontoTypeS, NullishOrSSchema.ts#L9833-L9835。这意味着返回的 schema 同时具备可编码类型为OptionT、可解码输入类型为T | undefined或T | null | undefined的双向类型信息TypeScript 可以据此自动推导Schema.decode*与Schema.encode*的入参类型无需手动断言。测试用例验证行为以测试为准两个构造器的行为在 Schema.test.ts 中有完整的断言覆盖这里摘录关键用例OptionFromUndefinedOrSchema.test.ts#L2917-L2933const schema Schema.OptionFromUndefinedOr(Schema.FiniteFromString) await decoding.succeed(undefined, Option.none()) // undefined - None await decoding.succeed(1, Option.some(1)) // 1 - Some(1) await decoding.fail(a, Expected a finite number) // 非法值仍走内层校验 await encoding.succeed(Option.none(), undefined) // None - undefined await encoding.succeed(Option.some(1), 1) // Some(1) - 1注意内层 schemaFiniteFromString的校验在Some分支中仍然生效a这类非法输入会以Expected a finite number失败而不是被静默吞掉。OptionFromNullishOrSchema.test.ts#L2935-L2973测试覆盖了onNoneEncoding: null与onNoneEncoding: undefined两条路径// onNoneEncoding: null const schema Schema.OptionFromNullishOr(Schema.FiniteFromString, { onNoneEncoding: null }) await decoding.succeed(null, Option.none()) // null - None await decoding.succeed(undefined, Option.none()) // undefined - None await decoding.succeed(1, Option.some(1)) // 1 - Some(1) await decoding.fail(a, Expected a finite number) await encoding.succeed(Option.none(), null) // None - null await encoding.succeed(Option.some(1), 1) // Some(1) - 1 // onNoneEncoding: undefined await encoding.succeed(Option.none(), undefined) // None - undefined await encoding.succeed(Option.some(1), 1)两个用例的解码断言完全一致唯一的差异体现在None的编码结果上——这正是onNoneEncoding存在的意义。测试还同时调用了asserts.arbitrary().verifyGeneration()说明这些构造器参与 Effect Schema 的任意值生成Arbitrary机制可用于属性测试场景。完整可运行示例将上面的行为汇总为可直接运行的代码来自源码 docstring 与测试的组合import { Option, Schema } from effect // 1) 只把 undefined 视为缺省null 保留为 Some(null) const undefinedOr Schema.OptionFromUndefinedOr(Schema.String) Schema.decodeSync(undefinedOr)(undefined) // Option.none() Schema.decodeSync(undefinedOr)(hello) // Option.some(hello) Schema.decodeSync(undefinedOr)(null) // Option.some(null)null 不是 undefined // 2) null / undefined 都视为缺省默认编码回 undefined const nullishOr Schema.OptionFromNullishOr(Schema.String) Schema.decodeSync(nullishOr)(null) // Option.none() Schema.decodeSync(nullishOr)(undefined) // Option.none() Schema.encodeSync(nullishOr)(Option.none()) // undefined // 3) 需要 None 编码为 null 时显式配置 const nullishOrNull Schema.OptionFromNullishOr(Schema.String, { onNoneEncoding: null }) Schema.encodeSync(nullishOrNull)(Option.none()) // null // 4) 与 Struct 组合使用字段级缺省语义 const Profile Schema.Struct({ nickname: Schema.OptionFromNullishOr(Schema.String), age: Schema.OptionFromUndefinedOr(Schema.Number) })与相关 API 的横向对比与选型这几个构造器都围绕把可能缺省的值折叠进 Option但判空边界各不相同。下表基于 Schema.ts 中各构造器的注释与测试整理构造器解码时视为None编码None为适用场景OptionFromNullOrL9791仅nullnull数据库字段、JSON 中null表示缺省OptionFromUndefinedOrL9820仅undefinedundefinedJS 运行时可选参数、API 请求体中的缺省字段OptionFromNullishOrL9850null与undefinedundefined默认或null混合来源数据两端都能容忍OptionFromOptionalKeyL9883缺失的 key缺失 key不输出该字段Struct 中整键可选且要保留字段名OptionFromOptionalL9913缺失 key 或值为undefined缺失 keyStruct 中键存在但可能为 undefinedOptionFromOptionalNullOrL9946缺失 key、null、undefinedomit/null/undefined三选一全缺省语义 编码形式完全可控选型建议字段可能整键不存在用OptionFromOptionalKey编码为省略 key或OptionFromOptional把显式undefined也折叠掉字段键必然存在但值可能是null用OptionFromNullOr字段键必然存在但值可能是undefined用OptionFromUndefinedOr它不会误伤null语义字段同时可能为null或undefined用OptionFromNullishOr并按目标格式用onNoneEncoding决定None的落盘形式需求覆盖缺失 key / null / undefined全部情况且编码形式需精细控制考虑 v4 的OptionFromOptionalNullOr。在 t3code 仓库中的定位effect-smol 是 t3code 仓库以.repos子目录方式引入的参考源码仓库位于 .repos/effect-smol其中packages/effect即 Effect 核心库本体package.json 中版本为4.0.0-rc.112。本文涉及的Schema/Option/SchemaTransformation模块是 Effect 类型安全数据建模的核心组成OptionFromUndefinedOr与OptionFromNullishOr正是其可选值建模能力的一部分。仓库内同时维护了完整的 migration/schema.md 迁移指南与 ai-docs 系列文档可作为继续深入 Schema 体系如decodeTo、transform、Union等底层机制的入口。小结OptionFromUndefinedOr与OptionFromNullishOr是 Effect Schema 面向真实世界数据的两个实用补丁语义明确前者只把undefined折叠为Nonenull保留后者把null与undefined都折叠为None编码可控OptionFromNullishOr通过onNoneEncoding在null与undefined之间选择None的编码形态校验不丢失Some分支仍完整经过内层 schema 的解析与校验测试完备解码、编码、Arbitrary 生成均在 Schema.test.ts 中有断言覆盖。在编写 Schema 时遇到可为空的字段优先用这些内置构造器收敛判空逻辑能让你的 schema 定义更接近领域语义也让解码/编码行为在测试中可被验证。【免费下载链接】t3code项目地址: https://gitcode.com/GitHub_Trending/t3/t3code创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
返回列表