资讯动态

Effect 时长字符串统一解码:解读 `Schema.DurationFromString` 与 `Duration.fromInput` 的 Infinity 支持

发布时间:2026/9/15 16:52:16 来源:尧图企业网站定制
Effect 时长字符串统一解码解读Schema.DurationFromString与Duration.fromInput的 Infinity 支持【免费下载链接】effectBuild production-ready applications in TypeScript项目地址: https://gitcode.com/GitHub_Trending/ef/effect本篇技术指南围绕effect仓库中一份 patch 变更集.changeset/pre/late-lamps-care.md展开系统讲解 Effect 如何将人类可读的时长字符串如10 seconds、500 millis、Infinity统一解码为Duration值。文章会依次剖析Duration.fromInput的字符串解析规则、新增的Schema.DurationFromString及底层SchemaTransformation.durationFromString实现以及Config.Duration如何借助共享 schema codec 简化配置解析。读完本文你将掌握在 Effect 中从字符串构造时长、用 Schema 解码校验配置以及处理正负无穷时长的最佳实践。变更集概述一次面向时长编解码的整合effect包的这份变更集以patch形式发布核心内容可拆解为四点新增Schema.DurationFromStringschema将字符串解码为Duration新增SchemaTransformation.durationFromString作为上述 schema 的底层转换实现扩展Duration.fromInput使其接受Infinity与-Infinity两个特殊字符串字面量简化Config.Duration的配置解析——将其内部实现收敛为对共享 schema codecSchema.DurationFromString的复用消除了此前重复的解析逻辑并顺带关闭了 issue #2092。从整体架构看这次变更的本质是把字符串 → Duration的解析能力统一沉淀为可复用的 codec让 Schema 解码、Config 读取、运行时构造三处共享同一套解析规则避免各模块各自实现一遍解析逻辑而产生行为漂移。Duration.fromInput字符串到时长的统一入口在 packages/effect/src/Duration.ts 中Duration.Input联合类型定义了所有可以被转换为Duration的输入形态Duration.ts#L172-L180export type Input | Duration | number // millis | bigint // nanos | readonly [seconds: number, nanos: number] | ${number} ${Unit} | Infinity | -Infinity | DurationObject可以看到字符串输入被划分为两类Infinity/-Infinity特殊字面量以及${number} ${Unit}形式的人类可读时长如10 seconds。这正是本次变更的核心之一——Duration.fromInput现在可以处理正负无穷。字符串解析的底层实现fromInputUnsafe对字符串分支的处理逻辑如下Duration.ts#L248-L285case string: { if (input Infinity) { return infinity } if (input -Infinity) { return negativeInfinity } const match DURATION_REGEXP.exec(input) if (!match) break // ... 按单位分支换算为毫秒或纳秒 }字符串匹配依赖一个集中定义的正则Duration.ts#L214const DURATION_REGEXP /^(-?\d(?:\.\d)?)\s(nanos?|micros?|millis?|seconds?|minutes?|hours?|days?|weeks?)$/该正则有三个值得注意的细节支持小数与负数数值部分允许-?\d(?:\.\d)?因此-1.5 seconds这类输入也可以被解析单复数均可单位部分s?表示nano/nanos、second/seconds等单复数形式都被接受完整时间单位覆盖从纳秒nano/nanos到周week/weeks共八档单位其中纳秒、微秒单位按纳秒精度换算分别乘1n、1_000n其余单位统一以毫秒为基准换算。Infinity与-Infinity的匹配被放在正则之前优先处理因为无穷值不满足${number} ${Unit}的形态属于独立字面量分支。Duration.infinity与Duration.negativeInfinity在内部表示为{ _tag: Infinity }与{ _tag: NegativeInfinity }两个特殊值Duration.ts#L348-L349。安全版本fromInput与抛出版本fromInputUnsafefromInput是fromInputUnsafe的不抛异常封装Duration.ts#L343-L345export const fromInput: (u: Input) Option.OptionDuration Option.liftThrowable( fromInputUnsafe )即解析失败如invalid时返回Option.none()成功时返回Option.some(duration)。这一设计决定了它在 Schema 解码中的角色fromInput不直接抛错而是以Option形式暴露失败供上层 codec 决定如何构造类型化错误。值得注意的是fromInputUnsafe除了字符串外还处理数值毫秒、bigint纳秒、[seconds, nanos]二元组与DurationObject。元组分支同样对无穷做了支持任一元素为-Infinity返回negativeInfinity为Infinity返回infinityDuration.ts#L297-L301。DurationObject则支持weeks、days、hours、minutes、seconds、milliseconds、microseconds、nanoseconds八个可选字段的叠加Duration.ts#L203-L212。Schema.DurationFromString字符串时长的类型化 codec底层转换SchemaTransformation.durationFromString在 packages/effect/src/Schema.ts 中durationFromString是一个transformEffect类型的转换Schema.ts#L11995-L12010const durationFromString: SchemaTransformation.TransformationDuration_.Duration, string SchemaTransformation .transformEffectDuration_.Duration, string({ decode: (s, options) Option_.match(Duration_.fromInput(s as Duration_.Input), { onNone: () Effect.fail( new SchemaIssue.InvalidValue( { expected: a valid Duration string }, s, options ) ), onSome: Effect.succeed }), encode: (duration) Effect.succeed(globalThis.String(duration)) })解码方向string → Duration复用Duration.fromInput用Option.match将失败路径转换为类型化的SchemaIssue.InvalidValue错误期望信息为a valid Duration string。编码方向Duration → string则直接调用Duration的toStringDuration.ts#L370-L381其中无穷值分别输出为Infinity/-Infinity纳秒值输出如5000 nanos毫秒值输出如5000 millis。这意味着解码与编码是严格互逆的能解码的字符串一定能被编码回去。DurationFromStringschema 的组装DurationFromString由基础Stringschema 与上述转换组合而成Schema.ts#L12111-L12138const DurationString String.annotate({ expected: a string that will be decoded as a Duration }) export const DurationFromString: DurationFromString DurationString.pipe( decodeTo(Duration, durationFromString) )Duration本身是一个declareschemaSchema.ts#L12061-L12110其默认 JSON 编解码采用带_tag的 tagged union 表示Infinity/NegativeInfinity/Nanos bigint 值 /Millis int 值。DurationFromString通过decodeTo建立字符串 ↔ Duration之间的双向转换属于Duration的字符串化表示。测试用例印证packages/effect/test/schema/Schema.test.ts 中为该 schema 编写了完整的双向断言Schema.test.ts#L5550-L5567解码成功1000 millis → Duration.millis(1000)、1 second → Duration.seconds(1)、Infinity → Duration.infinity、-Infinity → Duration.negativeInfinity解码失败value报错Expected a valid Duration string编码成功Duration.zero → 0 millis、Duration.seconds(5) → 5000 millis、Duration.nanos(5000n) → 5000 nanos、Duration.infinity → Infinity、Duration.negativeInfinity → -Infinity。这些用例覆盖了有限值、零值、纳秒精度和正负无穷是理解该 schema 行为边界的最直接依据。Duration schema 家族FromString/FromNanos/FromMillis与DurationDurationFromString并非孤立存在它和另外两个表示转换 schema 共同构成 Duration 的 schema 家族Schema外部表示解码方向编码限制DurationFromStringstring字符串 →Duration恒可编码借助toStringDurationFromNanosbigintbigint 纳秒 →Duration无穷值无法表示编码失败DurationFromMillisnumbernumber 毫秒 →Duration恒可编码Durationtagged union 对象结构体 →Duration恒可编码其中DurationFromNanos的编码在遇到Duration.infinity或Duration.negativeInfinity时会产生Expected a Duration representable as a bigint错误Schema.ts#L12011-L12026、Schema.test.ts#L5586-L5587。这形成了一个有意义的对比字符串表示是唯一能无损往返表达无穷时长的文本 codec而纳秒 bigint 表示则天然无法承载无穷。因此在需要将超时、TTL 等可能为无穷的配置以文本形式持久化或传输时DurationFromString是更合适的选择。Config.Duration围绕共享 codec 的简化变更前的痛点与变更后的实现此前配置模块需要自行维护一套时长字符串解析逻辑与Duration.fromInput的能力存在重复。本次变更将Config.Duration的实现收敛为一行委托Config.ts#L1357-L1359export function Duration(name?: string) { return schema(Schema.DurationFromString, name) }从源码结构看它现在完全复用Config.schema(Schema.DurationFromString, name)这条通用路径——所有字符串配置都可以用同一个schema入口配合不同 codec 完成解码Duration配置不再有特殊分支Config.ts#L1319-L1359。这就是变更集中simplify config duration parsing around the shared schema codec的具体含义。使用示例从环境变量读取超时配置Config.Duration接受任何Duration.fromInput能解析的字符串官方 JSDoc 示例展示了与ConfigProvider.fromEnv配合的完整链路Config.ts#L1334-L1350import { Config, ConfigProvider, Duration, Effect } from effect const program Config.Duration(DURATION).pipe(Effect.map(Duration.toMillis)) const provider ConfigProvider.fromEnv({ env: { DURATION: 10 seconds } }) Effect.runSync( program.pipe(Effect.provideService(ConfigProvider.ConfigProvider, provider)) ) // 10000得益于本次变更该示例中环境变量DURATION的值现在还可以是500 millis、Infinity表示无限超时或-Infinity表示负无穷因为Config.Duration底层的DurationFromString完整继承了Duration.fromInput的全部解析能力。这在配置超时、重试间隔、缓存 TTL 等场景下非常实用——例如用Infinity表达永不超时/永不过期的语义。实践建议运行时构造需要将外部输入如 HTTP 参数安全转换为Duration时优先使用Duration.fromInput返回Option仅在可信来源或允许抛错的场景使用Duration.fromInputUnsafe边界数据校验使用Schema.DurationFromString作为 DTO 或配置的 schema 时注意解码失败统一为Expected a valid Duration string错误可借此实现统一的参数校验反馈无穷时长的传输若业务需要表达无限语义无限重试、无限超时字符串形式的Infinity/-Infinity是可持久化、可往返的唯一文本表示选择DurationFromNanos作为 codec 时编码无穷值会失败需提前规避配置读取读取环境变量或配置文件中的时长设置直接使用Config.Duration(KEY)即可获得与 Schema 一致的解析行为无需额外编写字符串拆分逻辑。小结本次 patch 变更以Schema.DurationFromString为纽带将Duration.fromInput的字符串解析、SchemaTransformation.durationFromString的类型化转换和Config.Duration的配置读取三处能力统一到了一套共享 codec 之上同时为Duration.fromInput补齐了Infinity与-Infinity两个无穷字面量。对使用者而言这意味着一个字符串从配置读取到运行时构造再到 schema 校验全程遵循同一套解析规则且无穷时长在文本表示下可无损往返。相关实现与验证可分别在 Duration.ts、Schema.ts、Config.ts 与 Schema.test.ts 中进一步研读。【免费下载链接】effectBuild production-ready applications in TypeScript项目地址: https://gitcode.com/GitHub_Trending/ef/effect创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

读完文章,也想定制专属网站?

尧图设计师 24 小时内与您沟通定制方案

免费获取报价