如何在TypeScript中实现类似z.infer<>的泛型获取器
实现类似Zod的z.infer<>/z.input<>/z.output<>工具类型
Zod这类工具类型的核心逻辑是:让每个Schema类内置类型元数据,再通过TypeScript的索引类型提取这些元数据。下面一步步拆解实现思路:
1. 核心基础:带类型标记的Schema类
所有Zod Schema的父类都会定义内部类型成员(比如_inferType),用来存储当前Schema对应的TS类型。先实现一个最简版的基类约定:
// 基础Schema接口,约定所有Schema必须包含的类型标记 interface Schema<TInfer = unknown, TInput = TInfer, TOutput = TInfer> { // 存储最终推断出的类型 _inferType: TInfer; // 存储输入接受的类型(比如coerce类型的input是原始类型,output是转换后的类型) _inputType: TInput; // 存储输出的类型 _outputType: TOutput; } // 字符串Schema实现 class StringSchema implements Schema<string> { _inferType!: string; _inputType!: string; _outputType!: string; // 模拟Zod的链式方法,比如optional() optional() { return new OptionalSchema(this) as Schema<string | undefined>; } } // 可选类型Schema实现 class OptionalSchema<T extends Schema> implements Schema< T['_inferType'] | undefined, T['_inputType'] | undefined, T['_outputType'] | undefined > { _inferType!: T['_inferType'] | undefined; _inputType!: T['_inputType'] | undefined; _outputType!: T['_outputType'] | undefined; constructor(private schema: T) {} }
2. 实现工具类型:提取元数据
通过索引类型查询,直接提取Schema实例类型中的内部标记,就能实现和Zod一致的工具类型:
// 对应z.infer<> type Infer<T extends Schema> = T['_inferType']; // 对应z.input<> type Input<T extends Schema> = T['_inputType']; // 对应z.output<> type Output<T extends Schema> = T['_outputType'];
3. 验证效果
测试一下这些工具类型的实际作用:
const stringSchema = new StringSchema(); type StringType = Infer<typeof stringSchema>; // 类型为string const optionalStringSchema = stringSchema.optional(); type OptionalStringType = Infer<typeof optionalStringSchema>; // 类型为string | undefined
4. 进阶:处理带转换的Schema(比如coerce)
Zod的input和output主要区别体现在带转换的场景,比如z.coerce.number()的输入可以是字符串/布尔值,输出是数字。我们来实现这个场景:
class CoerceNumberSchema implements Schema<number, string | number | boolean, number> { _inferType!: number; // 输入支持字符串、数字、布尔值 _inputType!: string | number | boolean; // 输出是转换后的数字 _outputType!: number; } const coerceNumber = new CoerceNumberSchema(); type CoerceNumberInput = Input<typeof coerceNumber>; // string | number | boolean type CoerceNumberOutput = Output<typeof coerceNumber>; // number
关键原理总结
- 每个Schema类必须显式声明
_inferType/_inputType/_outputType三个类型成员,用来存储对应TS类型 - 工具类型本质是索引类型查询,直接提取Schema实例类型中的内部标记
- 链式方法(比如
optional()、coerce)会返回新的Schema类,其内部类型成员基于原Schema的类型修改
内容的提问来源于stack exchange,提问作者sqmasep
相关产品推荐
相关产品推荐

