如何根据输入动态调整函数参数类型?(仿TRPC实现)
实现类似TRPC的Procedure功能:根据Schema动态控制Input参数
需求概述
- 目标:复刻TRPC中
router.query和router.mutation的核心参数控制逻辑 - 现有
createProcedure方法:接收一个包含以下属性的配置对象schema:可选的输入验证器,定义输入结构resolver:业务处理函数,需接收包含验证后输入的对象(属性名为input)
- 核心约束:
- 仅当配置中传入
schema时,resolver的参数才会提示存在input属性 - 仅当配置了
schema时,createProcedure返回的函数才允许传入input参数
- 仅当配置中传入
第一次尝试代码(存在类型漏洞)
// 模拟Schema类型,简化验证逻辑 type Schema<T = unknown> = { parse: (input: unknown) => T }; declare function createProcedure<TInput>(opts: { schema?: Schema<TInput>; resolver: (args: { input?: TInput }) => Promise<unknown>; }): (args?: { input?: TInput }) => Promise<unknown>; // 问题1:无schema时,resolver仍能访问args.input(不应被允许) const proc1 = createProcedure({ resolver: async (args) => { console.log(args.input); // 此处错误提示缺失 return "ok"; }, }); // 问题2:无schema时,返回的函数仍允许传入input参数(不应被允许) proc1({ input: "test" }); // 此处错误提示缺失
第二次尝试(使用联合类型修复)
// 模拟Schema类型 type Schema<T = unknown> = { parse: (input: unknown) => T }; // 分情况定义配置类型:有schema/无schema type ProcedureOpts<TInput> = | { schema: Schema<TInput>; resolver: (args: { input: TInput }) => Promise<unknown> } | { schema?: never; resolver: (args: {}) => Promise<unknown> }; // 分情况定义返回函数类型 type ProcedureReturn<TInput> = TInput extends never ? () => Promise<unknown> : (args: { input: TInput }) => Promise<unknown>; declare function createProcedure<TInput>(opts: ProcedureOpts<TInput>): ProcedureReturn<TInput>; // 测试:无schema场景 const proc1 = createProcedure({ resolver: async (args) => { // @ts-expect-error 此处应报错:args不存在input属性 console.log(args.input); return "ok"; }, }); proc1(); // 正确:无参数 // @ts-expect-error 此处应报错:不允许传入input proc1({ input: "test" }); // 测试:有schema场景 const stringSchema: Schema<string> = { parse: (input) => String(input) }; const proc2 = createProcedure({ schema: stringSchema, resolver: async (args) => { console.log(args.input.toUpperCase()); // 正确:input为string类型 return "ok"; }, }); proc2({ input: "test" }); // 正确:必须传入input // @ts-expect-error 此处应报错:必须传入input参数 proc2();
方案说明
通过联合类型拆分配置结构,将createProcedure的入参分为两种互斥的情况:
- 存在
schema时:强制resolver接收带input的参数,返回的函数也要求必须传入input - 不存在
schema时:resolver仅接收空参数对象,返回的函数不允许传任何参数
配合条件类型动态生成返回函数的签名,确保TypeScript的类型提示严格匹配配置是否包含schema的场景,完全满足需求中的参数控制逻辑。
内容的提问来源于stack exchange,提问作者demetrius_mp
相关产品推荐
相关产品推荐

