Effect Schema 的 transform 怎么写自定义转换?
接口返回的金额是字符串 "19.9",内部计算却要数字。如果每个用到它的地方都自己写一遍 Number(x),转换就会散落在整个代码库里。
自定义转换用 Schema.decodeTo 搭配 SchemaTransformation.transform,同时给出 decode 和 encode 两个方向,转换就集中在 Schema 里。
先交代一个前置概念:Effect 的 Schema 是双向的,Type 是内部形态,Encoded 是外部形态,transform 定义两者怎样互相转换。Schema 的基本校验见 Effect Schema 基础,transform 在建模中的位置见 用 Effect Schema 怎么把领域规则写进类型?。下面先看内置转换,再看自己怎么写。
有没有现成的转换可用?
有,例如 Schema.NumberFromString:外部是字符串,内部是数字。
import { Schema } from "effect"
Schema.decodeUnknownSync(Schema.NumberFromString)("42") // 42
Schema.encodeSync(Schema.NumberFromString)(42) // "42"解码把 "42" 变成 42,编码再变回 "42"。
自己的转换怎么写?
用 Schema.decodeTo 搭配 SchemaTransformation.transform,给出解码与编码两个方向:
import { Schema, SchemaTransformation } from "effect"
const Trimmed = Schema.String.pipe(
Schema.decodeTo(
Schema.String,
SchemaTransformation.transform({
decode: (s: string) => s.trim(),
encode: (s: string) => s
})
)
)读法是「从 String 解码到 String,解码时去掉首尾空白,编码时原样输出」。同样的思路可以用来把 ISO 字符串变成日期对象,或者把逗号分隔字符串变成数组。
转换应该放在哪里?
放在边界的 Schema 里,而不是业务代码里。边界之外是外部表示,边界之内是领域表示,Schema 是两者之间唯一的翻译处。这样内部代码拿到的永远是已转换好的值,不需要在业务里写 new Date(x) 或 Number(x) 这类零散转换。
转换里能做 IO 吗?
不能。转换要遵守一条纪律:它应该是纯的、可预期的。需要查库校验的规则,不属于 Schema 的转换。
示例代码取自我整理的教程章节,没有标注 effect 版本号,这次改写中我也没有重新运行;API 写法以所用 effect 版本的文档为准。