TypeScript中如何为字典记录值实现Zod Schema匹配的泛型约束
问题描述
我有如下对象:
import zod from 'zod'; const f1 = (params: { a: string }) => ({ b: params.a }); // some function const f2 = () => {}; // some function const dir = { // ok register: { method: f1, // some function inputSchema: zod.object({ a: zod.string() }), outputSchema: zod.object({ b: zod.string() }) }, // not ok view: { method: f2, // some function inputSchema: zod.object({ c: zod.string() }), outputSchema: zod.object({ d: zod.string() }) } };
我希望为该字典添加类型约束,确保method函数的参数类型匹配zod.infer<typeof inputSchema>,返回值类型匹配zod.infer<typeof outputSchema>。
我尝试了以下实现,但TypeScript未抛出预期错误:
const f1 = (params: { a: string }) => ({ b: params.a }); // some function const f2 = () => {}; // some function interface RecordValue< P extends zod.Schema = zod.Schema, Q extends zod.Schema = zod.Schema > { method: (params: zod.infer<P>) => zod.infer<Q>; inputSchema: P; outputSchema: Q; } const d: { [key: string]: RecordValue } = { register: { method: f1, // some function inputSchema: zod.object({ a: zod.string() }), outputSchema: zod.object({ b: zod.string() }) }, view: { method: f2, // some function inputSchema: zod.object({ c: zod.string() }), outputSchema: zod.object({ d: zod.string() }) } };
请问该如何正确实现这类类型约束?
解决方案
问题出在直接使用RecordValue时,泛型被默认的zod.Schema填充,TypeScript无法为每个条目推断具体的输入输出Schema类型,导致约束失效。需要通过泛型函数来创建字典,让TypeScript自动推导每个条目的泛型参数,同时校验method的参数和返回值匹配性。
正确实现代码
import zod from 'zod'; const f1 = (params: { a: string }) => ({ b: params.a }); const f2 = () => {}; // 定义单个处理器条目的类型约束 type HandlerEntry<P extends zod.Schema, Q extends zod.Schema> = { method: (params: zod.infer<P>) => zod.infer<Q>; inputSchema: P; outputSchema: Q; }; // 用泛型函数包裹字典创建过程,让TypeScript自动校验每个条目 const createHandlers = <T extends Record<string, HandlerEntry<any, any>>>(handlers: T) => handlers; // 此时TypeScript会对不匹配的条目抛出错误 const dir = createHandlers({ register: { method: f1, inputSchema: zod.object({ a: zod.string() }), outputSchema: zod.object({ b: zod.string() }) }, view: { method: f2, // ❌ 此处会报错:f2的参数/返回值与inputSchema、outputSchema不匹配 inputSchema: zod.object({ c: zod.string() }), outputSchema: zod.object({ d: zod.string() }) } });
实现原理
- 泛型函数
createHandlers会接收一个对象T,TypeScript会自动推断T中每个属性对应的HandlerEntry的具体泛型参数(即输入SchemaP和输出SchemaQ)。 - 针对每个条目,TypeScript会严格对比
method的参数类型是否等于zod.infer<P>,返回值类型是否等于zod.infer<Q>,不匹配时直接抛出类型错误。
额外优化(避免any)
如果想更严谨,可以用映射类型明确约束字典结构:
type HandlerEntry<P extends zod.Schema, Q extends zod.Schema> = { method: (params: zod.infer<P>) => zod.infer<Q>; inputSchema: P; outputSchema: Q; }; type HandlersDict = Record<string, HandlerEntry<zod.Schema, zod.Schema>>; const createHandlers = <T extends HandlersDict>(handlers: T) => handlers;
这样同样能达到约束效果,同时避免直接使用any。
内容的提问来源于stack exchange,提问作者Atharva
相关产品推荐
相关产品推荐

