运行时如何区分tRPC的DecoratedMutation与DecoratedQuery?
运行时如何区分tRPC的DecoratedMutation与DecoratedQuery?
我之前也踩过这个坑!tRPC React客户端返回的DecoratedQuery和DecoratedMutation都是用Proxy包装的,所以直接检查useQuery或useMutation属性是否存在根本没用——Proxy会拦截所有属性访问,不管实际有没有都会返回一个占位函数,导致你看到的结果全是true。
下面给你两个靠谱的解决方案,按推荐程度排序:
方案一:给服务端Procedure添加Meta元数据(最可控、最兼容)
这是我最推荐的方法,完全不受tRPC版本或Proxy实现的影响,自己能完全掌控判断逻辑。
- 修改服务端的router定义,给每个Procedure加上
meta字段标记类型:
// app/trpc.ts import { initTRPC } from '@trpc/server'; import { createTRPCReact } from '@trpc/react-query'; import { z } from 'zod'; const t = initTRPC.create(); export const appRouter = t.router({ hello: t.procedure .meta({ procedureType: 'query' }) // 标记为查询类型 .query(() => 'world'), add: t.procedure .input(z.object({ x: z.number(), y: z.number() })) .meta({ procedureType: 'mutation' }) // 标记为突变类型 .mutation(({ input }) => input.x + input.y), }); // 其余代码保持不变 export type AppRouter = typeof appRouter; export const trpc = createTRPCReact<AppRouter>();
- 在客户端写两个运行时判断函数:
// 可以放在app/trpc.ts或者单独的工具文件里 function isDecoratedQuery(procedure: unknown): boolean { return (procedure as any)?._def?.meta?.procedureType === 'query'; } function isDecoratedMutation(procedure: unknown): boolean { return (procedure as any)?._def?.meta?.procedureType === 'mutation'; }
- 然后在组件里测试:
// app/page.tsx 'use client'; import React from 'react'; import { trpc } from './trpc'; export default function Page() { const helloProcedure = trpc.hello; const addProcedure = trpc.add; console.log('helloProcedure 是查询吗?', isDecoratedQuery(helloProcedure)); // true console.log('addProcedure 是突变吗?', isDecoratedMutation(addProcedure)); // true return ( <div> <h1>Minimal Reproducible Example Application</h1> <p>Open console to see the correct checks.</p> </div> ); }
方案二:直接检查客户端Procedure的_def内部字段(依赖tRPC实现,更简洁)
如果你的tRPC 11.x版本允许客户端访问_def字段,可以直接通过检查_def里是否存在query或mutation函数来判断,不需要修改服务端代码:
// 运行时判断函数 function isDecoratedQuery(procedure: unknown): boolean { return typeof (procedure as any)?._def?.query === 'function'; } function isDecoratedMutation(procedure: unknown): boolean { return typeof (procedure as any)?._def?.mutation === 'function'; }
直接在组件里用这两个函数就行,测试结果会和预期一致。不过要注意:这个方法依赖tRPC客户端的内部实现,如果未来tRPC调整了_def的结构,可能会失效,所以追求长期稳定性的话,还是方案一更稳妥。
为什么你原来的方法没用?
再补一句踩坑的原因:tRPC客户端为了实现类型安全的自动提示,用Proxy包装了所有Procedure。当你访问任何属性(比如useMutation)时,Proxy的get陷阱会自动返回一个占位函数,所以不管是Query还是Mutation,useQuery/useMutation都会存在——但你真的调用这些函数时,Query类型的Procedure调用useMutation会报错,反之亦然。
内容来源于stack exchange
相关产品推荐
相关产品推荐

