Effect Schema 新增 `OptionFromUndefinedOr` 与 `OptionFromNullishOr`:将可选字段安全解码为 `Option`
2026/9/14 14:16:25 网站建设 项目流程

Effect Schema 新增OptionFromUndefinedOrOptionFromNullishOr:将可选字段安全解码为Option

【免费下载链接】effectBuild production-ready applications in TypeScript项目地址: https://gitcode.com/GitHub_Trending/ef/effect

本文基于effect仓库中.changeset/pre/add-schema-option-from-undefined-nullish.md变更记录,系统讲解 Effect Schema 新增的OptionFromUndefinedOrOptionFromNullishOr两个解码器/编码器(schema)的用途、语义、底层实现与实战用法,帮助你在解析 API 响应、配置文件或数据库记录时,把undefined/null这类"缺省"值统一建模为类型安全的Option<T>

变更背景:为什么要把undefined/null映射为Option

在 TypeScript 中,undefinednull常用于表示"字段缺失"或"值不存在"。但在真实业务里它们往往语义模糊:一条 JSON 里某个键是缺失(undefined)、显式置空(null)还是真的没有值,调用方很难区分;而直接以T | undefinedT | null | undefined贯穿整个应用,又会让每个使用点都要重复做判空。

Effect 的Option<T>类型恰好用来显式表达"可能存在、也可能不存在":

  • Option.some(value)表示值存在;
  • Option.none()表示值不存在。

变更记录(.changeset/pre/add-schema-option-from-undefined-nullish.md)宣告在 Schema 模块中新增两个便捷 schema:

Schema: add `OptionFromUndefinedOr` and `OptionFromNullishOr` schemas.

它们与既有的OptionFromNullOr构成完整三元组,分别处理undefinednull、以及两者皆可(nullish)三种缺省语义,让"可选字段 →Option"的转换从手工拼接变换变成一行声明。

新增 API 概览

三个"从可选值构造 Option"的 schema 全部定义在 packages/effect/src/Schema.ts(自 3.10.0 起提供),签名与语义如下:

Schema输入(Encoded)解码结果(Type)说明
OptionFromNullOr<S>T \| nullOption<T>nullNone,其余 →Some
OptionFromUndefinedOr<S>T \| undefinedOption<T>undefinedNone,其余 →Some
OptionFromNullishOr<S>T \| null \| undefinedOption<T>null/undefinedNone,其余 →Some
  • OptionFromUndefinedOr对应 Schema.ts 中的 OptionFromUndefinedOr 定义,类型签名继承自decodeTo<Option<toType<S>>, UndefinedOr<S>>,并带有"Rebuild"元数据以支持组合重建。
  • OptionFromNullishOr对应 Schema.ts 中的 OptionFromNullishOr 定义,额外接受一个可选配置options.onNoneEncoding: null | undefined,用于指定None在编码回外部表示时的取值。

三者都接收一个普通 schemaS作为值类型,例如Schema.StringSchema.NumberSchema.FiniteFromString等,返回的是"可双向转换"的 schema——既能把外部宽松表示解码为Option,也能把Option编码回外部表示。

解码与编码语义

OptionFromUndefinedOrundefined与缺失等价

undefined在 JavaScript/TypeScript 中天然表示"未定义",与 JSON 中键缺失高度契合。该 schema 的语义(见 Schema.ts 中 OptionFromUndefinedOr 的 JSDoc):

  • 解码:undefinedOption.none();其他任何值 →Option.some(value)(随后按内部 schema 校验,如"1"1)。
  • 编码:Option.none()undefinedOption.some(value)value

OptionFromNullishOrnullundefined一视同仁

很多外部系统(如某些数据库、旧式 API)同时用nullundefined表示"无值"。OptionFromNullishOr将二者统一折叠为None(见 Schema.ts 中 OptionFromNullishOr 的 JSDoc):

  • 解码:nullundefinedOption.none();其余值 →Option.some(value)
  • 编码:Option.none()→ 由options.onNoneEncoding决定编码为null还是undefined默认值为undefinedOption.some(value)value

提示:若只想处理null而不想理会undefined,请使用既有的OptionFromNullOr;三个 schema 的边界划分正是为了让"缺省语义"与业务一一对应。

底层实现:decodeTo+ 变换原语

新增 schema 并非从零实现,而是建立在 Schema 既有变换机制之上。以 Schema.ts 中三个函数的实现 为例:

export function OptionFromNullOr<S extends Constraint>(schema: S): OptionFromNullOr<S> { return NullOr(schema).pipe(decodeTo( Option(toType(schema)), SchemaTransformation.optionFromNullOr() )) } export function OptionFromUndefinedOr<S extends Constraint>(schema: S): OptionFromUndefinedOr<S> { return UndefinedOr(schema).pipe(decodeTo( Option(toType(schema)), SchemaTransformation.optionFromUndefinedOr() )) } export function OptionFromNullishOr<S extends Constraint>( schema: S, options?: { onNoneEncoding: null | undefined } ): OptionFromNullishOr<S> { return NullishOr(schema).pipe(decodeTo( Option(toType(schema)), SchemaTransformation.optionFromNullishOr(options) )) }

其构成分三层:

  1. 宽松输入 schemaNullOr/UndefinedOr/NullishOr在 Schema.ts 中定义为对原 schema 与NullUndefined的 Union,先把外部表示"放行"为T | nullT | undefinedT | null | undefined

  2. decodeTo变换:将上述宽松表示转换为内部目标类型Option<toType<S>>

  3. 变换原语:真正执行Option与原始值互转的是 packages/effect/src/SchemaTransformation.ts 中的三个纯函数变换(@since 4.0.0):

    • optionFromNullOr<T>()(SchemaTransformation.ts 定义):decode: Option.fromNullOrencode: Option.getOrNull
    • optionFromUndefinedOr<T>()(SchemaTransformation.ts 定义):decode: Option.fromUndefinedOrencode: Option.getOrUndefined
    • optionFromNullishOr<T>(options?)(SchemaTransformation.ts 定义):decode: Option.fromNullishOrencode则根据options?.onNoneEncoding === null选择Option.getOrNull还是Option.getOrUndefined(默认后者)。

    从源码结构可以推断,OptionFromNullishOronNoneEncoding参数在底层就是编码分支的选择开关:传nullNone编码为null,否则编码为undefined。这些变换"纯且同步"(pure and synchronous),不引入异步或副作用。

这种分层设计带来的直接好处:你可以不依赖高层 schema,直接用手写的Schema.NullOr(Schema.String)+Schema.decodeTo(Schema.Option(Schema.String), SchemaTransformation.optionFromNullOr())组合出等价效果,在自定义、内联场景下更灵活。

实战用法

解析 API 响应中的可选字段

import { Option, Schema } from "effect" // 模拟外部返回:某些字段缺失、某些显式为 null const User = Schema.Struct({ id: Schema.Number, // 外部可能缺 key,也可能给 undefined nickname: Schema.OptionFromUndefinedOr(Schema.String), // 外部可能给 null 或 undefined deletedAt: Schema.OptionFromNullishOr(Schema.DateTimeUtcFromSelf) }) const decode = Schema.decodeUnknownSync(User) decode({ id: 1 }) // nickname: Option.none() decode({ id: 1, nickname: "alice" }) // nickname: Option.some("alice") decode({ id: 1, deletedAt: null }) // deletedAt: Option.none()

解码后nickname/deletedAt都是类型安全的Option,下游用Option.mapOption.getOrElse等操作即可安全取值,不必再手工判断undefined/null

控制None的编码回写

当需要把内部Option重新编码为外部表示时,OptionFromNullishOronNoneEncoding决定了None落到哪个值:

import { Option, Schema } from "effect" const schema = Schema.OptionFromNullishOr(Schema.String, { onNoneEncoding: null }) Schema.encodeSync(schema)(Option.some("v")) // => "v" Schema.encodeSync(schema)(Option.none()) // => null

若不传onNoneEncoding,则默认编码为undefined

const schema2 = Schema.OptionFromNullishOr(Schema.String) Schema.encodeSync(schema2)(Option.none()) // => undefined

提示:仓库内package.json"effect": patch标记表明该变更属于补丁级(非破坏性)更新,已有使用OptionFromNullOr的代码可以平滑升级,无需迁移改动。

测试验证与行为保证

新 API 的行为在 packages/effect/test/schema/Schema.test.ts 中有完整覆盖(自 3001 行起):

  • OptionFromUndefinedOr(Schema.test.ts#L3001-L3017):
    • 解码:undefinedOption.none()"1"Option.some(1)"a"→ 解码失败(Expected a finite number,说明内部 schema 校验仍然生效);
    • 编码:Option.none()undefinedOption.some(1)"1"
  • OptionFromNullishOr的两种配置(Schema.test.ts#L3019-L3057):
    • { onNoneEncoding: null }null/undefined均解码为NoneNone编码回null
    • { onNoneEncoding: undefined }:解码行为相同,None编码回undefined

测试同时验证了 Arbitrary 生成(asserts.arbitrary().verifyGeneration()),说明这两个 schema 也可以用于基于属性测试与数据生成场景。若你在自己的代码中使用,可以参照该文件里的断言方式验证解码/编码往返(round-trip)一致性。

总结

  • OptionFromUndefinedOr(schema):处理T | undefinedundefinedNone
  • OptionFromNullishOr(schema, { onNoneEncoding }):处理T | null | undefinednullundefined统一为None,编码回写可选nullundefined
  • 二者与OptionFromNullOr互补,底层复用 SchemaTransformation.ts 的纯函数变换,实现简洁、可组合;
  • 新 API 自3.10.0起可用(见 Schema.ts 中@since 3.10.0标注),属于补丁级变更,可直接升级引入。

需要声明:3.10.0的版本号来自源码中@since标注;由于当前仓库包含预发布 changeset,实际发布版本以官方发布渠道为准。

【免费下载链接】effectBuild production-ready applications in TypeScript项目地址: https://gitcode.com/GitHub_Trending/ef/effect

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询