如何修改io-ts Codec.struct中单个属性的Codec而无需重定义整个结构
问题
我有部分对象结构需要修改单个属性的Codec,举个例子:我通过COD.struct定义了一个包含日期字段的结构体,对接不同第三方系统API时,该日期字段的输入值有时为时间戳格式,有时为ISO字符串格式。请问是否可以在不重定义其余全部属性的前提下,将该字段的Codec从TimestampDateCodec替换为IsoStringDateCodec?
import * as COD from "io-ts/Codec"; const TimestampDateCodec: COD.Codec<unknown, number, Date> = {}; const IsoStringDateCodec: COD.Codec<unknown, string, Date> = {}; const current = COD.struct({ id: COD.string, // a lot of other props... someDate: TimestampDateCodec, // change this to IsoStringDateCodec without redefining the whole struct });
回答
可以实现,不需要重写全部属性,通过io-ts的类型组合能力即可完成,有两种常用实现方案:
方案1:基于已有结构体直接改造(无需改动原有定义)
适合已经在项目中多处引用原有结构体,不方便重构原有定义的场景,通过omit剔除要替换的字段,再用intersect合并新的字段定义即可:
import * as COD from "io-ts/Codec"; // omit工具可从io-ts-types引入,新版io-ts也内置了对应能力 import { omit } from "io-ts-types/lib/Codec/omit"; const TimestampDateCodec: COD.Codec<unknown, number, Date> = {}; const IsoStringDateCodec: COD.Codec<unknown, string, Date> = {}; // 原有定义完全不用改 const current = COD.struct({ id: COD.string, // 其他大量原有属性 someDate: TimestampDateCodec, }); // 仅替换someDate字段生成新的Codec const isoDateStruct = COD.intersect( omit(current, ["someDate"]) )( COD.struct({ someDate: IsoStringDateCodec }) );
如果不想引入io-ts-types的额外依赖,推荐使用方案2。
方案2:提前抽离公共属性(推荐前期定义阶段使用)
如果还在业务逻辑开发前期,推荐把公共属性单独抽离,后续扩展、修改字段都更易维护:
import * as COD from "io-ts/Codec"; const TimestampDateCodec: COD.Codec<unknown, number, Date> = {}; const IsoStringDateCodec: COD.Codec<unknown, string, Date> = {}; // 抽离所有公共属性 const commonProps = { id: COD.string, // 其他大量原有属性 } as const; // 时间戳格式版本结构体 const timestampDateStruct = COD.struct({ ...commonProps, someDate: TimestampDateCodec }); // ISO字符串格式版本结构体 const isoDateStruct = COD.struct({ ...commonProps, someDate: IsoStringDateCodec });
两种方案生成的新Codec解码后输出的类型完全兼容原有定义,仅输入的编码格式有差异,完全满足对接不同第三方接口的需求。
内容的提问来源于stack exchange,提问作者florian norbert bepunkt
相关产品推荐
相关产品推荐

