如何在io-ts中使用自定义联合类型进行运行时类型检查
解决io-ts中自定义联合类型数组的编码问题
你遇到的问题核心是混淆了TypeScript类型和io-ts的运行时codec,并且错误地使用了T.type()——这个函数是用来定义对象结构的codec,而不是处理字符串字面量联合的。下面给你两种正确的实现方式:
方法一:用T.literal + T.union创建联合类型Codec
因为你的MyUnion是字符串字面量的联合,我们可以为每个字面量创建literal codec,再通过union组合成完整的联合类型验证器:
import * as T from 'io-ts'; import * as E from 'fp-ts/Either'; import { pipe } from 'fp-ts/lib/function'; console.clear(); // 1. 定义TypeScript编译时类型 type MyUnion = 'item-a' | 'item-b' | 'item-c'; // 2. 创建对应的io-ts运行时验证器 const MyUnionCodec = T.union([ T.literal('item-a'), T.literal('item-b'), T.literal('item-c'), ]); // 3. 使用这个Codec定义数组结构 const test2 = T.type({ data: T.array(MyUnionCodec), }); // 测试合法输入 pipe(test2.decode({ data: ['item-a', 'item-c'] }), E.fold(console.error, console.log)); // 输出: { data: [ 'item-a', 'item-c' ] } // 测试非法输入(不在联合类型内的值) pipe(test2.decode({ data: ['hello'] }), E.fold(err => console.error("验证失败:", err), console.log)); // 会输出验证错误,因为'hello'不在MyUnion的允许值中
方法二:用T.keyof简化联合类型Codec
如果你的联合类型是基于某个对象的键,可以用T.keyof来更简洁地创建codec,同时保证类型和运行时验证器同步:
import * as T from 'io-ts'; import * as E from 'fp-ts/Either'; import { pipe } from 'fp-ts/lib/function'; console.clear(); // 1. 定义包含所有联合值的常量对象(运行时存在) const MyUnionOptions = { 'item-a': true, 'item-b': true, 'item-c': true, } as const; // 2. 从对象键推导TypeScript类型 type MyUnion = keyof typeof MyUnionOptions; // 3. 用T.keyof创建对应的codec const MyUnionCodec = T.keyof(MyUnionOptions); // 4. 定义数组结构 const test2 = T.type({ data: T.array(MyUnionCodec), }); // 测试效果和方法一完全一致 pipe(test2.decode({ data: ['item-b'] }), E.fold(console.error, console.log));
为什么你的原代码会报错?
T.type()的用途错误:T.type()接收的是一个对象,每个属性对应一个codec(比如T.type({ name: T.string })用来验证{ name: "xxx" }这样的对象),你传入MyUnion这个TypeScript类型是完全不匹配的。- 类型和codec的区别:TypeScript的类型只是编译时的类型检查标记,而io-ts的codec是运行时存在的验证逻辑,必须显式创建对应的codec才能让io-ts知道如何验证数据。
内容的提问来源于stack exchange,提问作者Radex
相关产品推荐
相关产品推荐

