You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.22 10:25:30