Effect Schema 数字验证规范化:Schema.Natural、Int/Finite 统一校验与 Duration 负时长支持 📅 发布时间:2026/9/13 10:13:12 👁 浏览次数: Effect Schema 数字验证规范化Schema.Natural、Int/Finite 统一校验与 Duration 负时长支持【免费下载链接】t3code项目地址: https://gitcode.com/GitHub_Trending/t3/t3code导读本文围绕 effect-smol 仓库中编号为canonical-number-schemas的变更记录展开系统讲解 Effect Schema 在数字类型验证上的一次重要规范化新增Schema.Natural非负安全整数并让Schema.Int、Schema.Finite、Schema.Natural成为全库数字域值的标准验证三件套同时覆盖日期、文件、时区、集群、事件日志、持久化、Socket、SQL、DevTools 等模块的边界值收紧。读完本文你将理解 Effect 数字 Schema 的验证层级设计、NumberFromString解码语义的修正细节以及DurationFromMillis/DurationFromNanos为何与如何支持负时长并能据此评估升级后的行为变化。一、变更背景一次数字验证语义的系统性收敛changeset 文件 .repos/effect-smol/.changeset/pre/canonical-number-schemas.md 声明了该变更波及的包范围受影响包变更级别effectpatcheffect/ai-anthropicpatcheffect/ai-openaipatcheffect/ai-openai-compatpatcheffect/ai-openrouterpatcheffect/openapi-generatorpatch从变更面可以看出这不是一次孤立的 Schema 工具函数调整而是一次标准验证语义下沉effect核心包先确立规范化的数字 Schema随后 AI 协议族Anthropic / OpenAI / OpenAI-compat / OpenRouter与 OpenAPI 代码生成器统一复用这些 Schema 来描述数值域如 token 数量、maxTokens、temperature 等数值字段避免各模块各自实现一套验证规则。对于使用这些包的开发者而言这意味着数字字段的校验行为将趋于一致且更严格。二、核心新增Schema.Natural——非负安全整数本次变更最直观的新增 API 是Schema.Natural。在 packages/effect/src/Schema.ts 中其定义为export const Natural: Natural Int.check(isGreaterThanOrEqualTo(0))也就是说Natural是在Int之上叠加大于等于 0约束的组合 Schema先通过Int的整数校验只接受安全整数非NaN、非±Infinity、无小数部分再通过isGreaterThanOrEqualTo(0)剔除所有负数。因此在 Effect 的语义里Natural 非负安全整数0、1、2、…、2^53 - 1 范围内的整数值。注意它并不等价于数学意义上的自然数部分数学约定中自然数从 1 开始0 是合法输入。对应的测试用例位于 packages/effect/test/schema/Schema.test.ts验证了完整的行为矩阵const decoding asserts.decoding() await decoding.succeed(0) // 0 合法 await decoding.succeed(1) // 正整数合法 await decoding.fail(-1, Expected a value greater than or equal to 0) // 负数被拒 await decoding.fail(1.1, Expected an integer) // 小数被拒 const encoding asserts.encoding() await encoding.succeed(0) await encoding.fail(-1, Expected a value greater than or equal to 0)错误消息同样具有层级性小数输入报Expected an integer来自Int负数输入报Expected a value greater than or equal to 0来自Natural的附加检查方便调用方快速定位是哪一层约束失败。同时测试还对Natural调用了asserts.arbitrary().verifyGeneration()确保任意值生成器property-based testing只产生满足约束的值。三、规范化数字 Schema 三件套Int/Finite/Natural本次变更的核心思路是让Int、Finite、Natural成为canonical规范化数字 Schema全库统一引用。三者在 packages/effect/src/Schema.ts 中的实现形成清晰的递进结构Schema源码定义语义校验失败示例Finitemake(SchemaAST.finite)L7684有限数值拒绝NaN、Infinity、-InfinityNaN、InfinityIntNumber.check(isInt())L8353安全整数隐含有限性1.5、NaNNaturalInt.check(isGreaterThanOrEqualTo(0))L8377非负安全整数-1这种基础约束 叠加约束的组合方式意味着验证语义天然可推理Int强于Finite整数必然有限Natural强于Int非负整数必然是整数。从源码结构看Natural的任意值生成与编码路径都复用了Int的规则只是额外施加了下界检查因此三者在 JSON Schema 输出、编解码、任意值生成等环节的底层行为保持一致。四、边界值收紧非有限与非整数值的全面拒绝changeset 明确指出日期date、date-time、文件file、时区time-zone、集群cluster、事件日志event-log、持久化persistence、Socket、SQL 和 DevTools 模块的 Schema现在会在适当位置拒绝非有限non-finite或非整数non-integer的输入。以时间与数值的交界处为例Schema.DateFromMillis的测试在 packages/effect/test/schema/Schema.test.ts 中验证了新的严格行为await decoding.fail(NaN, Expected an integer) await decoding.fail(Infinity, Expected an integer) await decoding.fail(-Infinity, Expected an integer)即毫秒时间戳必须为整数NaN/Infinity/-Infinity全部被拒绝——这防止了非法时间戳进入下游日期运算。同理从源码结构看Finite/Int/Natural在 eventlog如EventJournal、EventLogMessage、SqlEventJournal与 SQL 错误模块SqlError中被引入说明这些模块中表示序号、偏移量、长度、错误码等数值字段的 Schema 已切换到规范化验证杜绝NaN或小数混入持久化/网络边界。升级影响提示如果你的业务数据中曾出现过NaN、Infinity或非整数的时间戳/序号升级后这些值的解码decode将直接失败而非静默通过。建议在升级前对相关字段做数据清洗或为旧数据准备兼容的transformOrFail降级逻辑。五、NumberFromString解码 Schema 的修正变更还修正了Schema.NumberFromString的解码后 schemadecoded schema。在 packages/effect/src/Schema.ts 中其实现为export const NumberFromString: NumberFromString String.annotate({ expected: a string that will be decoded as a number }).pipe(decodeTo(Number, SchemaTransformation.numberFromString))它把字符串解码为 JSNumber含NaN、Infinity、-Infinity的字符串表示。修正点在于其类型层面的解码 schema 与运行时行为一致——对应测试在 packages/effect/test/schema/Schema.test.ts 中完整覆盖await decoding.succeed(1, 1) await decoding.succeed(NaN, NaN) await decoding.succeed(Infinity, Infinity) await decoding.succeed(Infinity, Infinity) await decoding.succeed(-Infinity, -Infinity) await encoding.succeed(1, 1) await encoding.succeed(NaN, NaN) await encoding.succeed(Infinity, Infinity) await encoding.succeed(-Infinity, -Infinity) await encoding.fail(a, Expected number)也就是说NumberFromString是宽松解码的它忠实反映 JSNumber的全部取值空间包括三个非有限特殊值。与之形成对照的是新增于同文件的FiniteFromStringL13281它同样基于SchemaTransformation.numberFromString但解码目标收紧为Finite会拒绝NaN、Infinity、-Infinity对应的字符串。实践建议解析用户输入或外部 API 返回的字符串数字时优先选用FiniteFromString以获得防御性校验只有当你确实需要保留NaN/Infinity语义如透传计算中间结果时才使用NumberFromString。六、DurationFromMillis/DurationFromNanos支持负时长本次变更的第三个行为点Schema.DurationFromMillis和Schema.DurationFromNanos现在允许表示负时长。这在Duration语义下是有实际意义的——负值可表达时间提前量如调度提前量、倒计时、时钟偏差补偿此前如果这些 Schema 对输入做非负约束这类场景就无法建模。从 packages/effect/src/Schema.ts 的实现可见DurationFromNanos以BigInt纳秒为输入载体经管道转换构建DurationDurationFromMillis以Number毫秒为输入载体同样支持负值。对应测试位于 packages/effect/test/schema/Schema.test.ts对DurationFromNanos与DurationFromMillis都做了正负双方向的解码/编码断言确认负输入如-1_000_000n纳秒或-1000毫秒能成功生成负时长对象且编码可逆。使用示例import { Schema } from effect const ms Schema.DurationFromMillis Schema.decodeSync(ms)(-250) // 成功-250ms 的 Duration Schema.decodeSync(ms)(1500) // 成功1500ms 的 Duration const ns Schema.DurationFromNanos Schema.decodeSync(ns)(-1_000_000n) // 成功-1ms 的 Duration需要注意的是负时长仍会受Duration类型自身运算规则约束如与其它 Duration 相加、与调度器交互时的取整/取绝对值行为本文讨论的是 Schema 编解码层面对负值的放行。七、迁移与验证建议综合本次变更升级effect及 AI 相关包时建议关注以下几点数字字段更严格Int、Finite、Natural以及日期、SQL、事件日志等领域的数值 Schema 开始拒绝NaN/Infinity/ 非整数值解码失败面扩大需要审查所有输入来源善用Natural凡是表示计数、序号、长度、token 数等不应为负的字段直接用Schema.Natural表达意图比Schema.Int 自定义谓词更规范也更容易被 AI 协议与 OpenAPI 生成器识别区分NumberFromString与FiniteFromString面向外部输入的字符串数字解析优先FiniteFromString负 Duration 合法化DurationFromMillis/DurationFromNanos现在可以表达负时长调度类逻辑若依赖旧的非负行为需显式加约束跑一遍 Schema 测试仓库内置的 Schema.test.ts 覆盖了Natural、NumberFromString、DurationFromMillis、DurationFromNanos等本次变更的全部行为矩阵可作为升级后的回归基线。参考路径变更记录.repos/effect-smol/.changeset/pre/canonical-number-schemas.mdSchema 核心实现packages/effect/src/Schema.tsFiniteL7684、IntL8353、NaturalL8377、DurationFromNanosL12504、DurationFromMillisL12537、NumberFromStringL13252、FiniteFromStringL13281Schema 行为测试packages/effect/test/schema/Schema.test.tsNaturalL9814、NumberFromStringL1931、DateFromMillisL1973、DurationFromNanos/DurationFromMillisL5448-L5475跨模块消费方示例EventJournal.ts、SqlError.ts、DevToolsSchema.ts【免费下载链接】t3code项目地址: https://gitcode.com/GitHub_Trending/t3/t3code创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考