tRPC端到端类型安全需正确配置tsconfig.json的baseUrl和paths、安装typescript-trpc-plugin插件、显式标注AppRouter类型、导出ServerRouter类型,并避免使用z.any()等不可推导类型。

tsconfig.json 里没配好 paths,VSCode 就看不到 tRPC 路由类型
tRPC 的端到端类型安全依赖 TypeScript 的模块解析能力。如果 tsconfig.json 没正确配置 baseUrl 和 paths,VSCode 就无法定位 trpc/client 或 trpc/server 的类型定义,补全直接失效。
常见错误现象:import { trpc } from '@/trpc/client' 报红,但编译不报错;trpc.post.create.useMutation() 没参数提示,hover 看不到类型。
-
baseUrl必须设为"."(项目根目录),不能是"./src"或留空 -
paths要覆盖所有 tRPC 相关路径,比如:{"@/trpc/*": ["src/trpc/*"], "@/server/*": ["src/server/*"]} - 确保
tsconfig.json在项目根目录,且 VSCode 打开的是该根目录(不是子文件夹) - 改完后必须重启 TS 服务:Ctrl+Shift+P → “TypeScript: Restart TS server”
VSCode 没启用 typescript-tslint-plugin 或 typescript-trpc-plugin
tRPC 的运行时类型推导(比如 router.input 自动映射到 mutation 参数)需要插件辅助。原生 TS 不识别 createRouter().query(...) 这类链式调用的类型传播逻辑。
目前最稳定的是社区维护的 typescript-trpc-plugin,它专为 tRPC v10+ 设计,能补全路由路径、输入输出类型、甚至错误形状。
- 安装:
npm install --save-dev typescript-trpc-plugin - 在
tsconfig.json的compilerOptions.plugins里加上:[{"name": "typescript-trpc-plugin"}] - 注意:该插件只作用于
.ts文件,.tsx中 hooks 使用需额外确认trpc/react-query版本兼容性 - 如果补全卡顿,可在插件配置加
"maxDepth": 2限制递归深度
trpc/client 初始化时没传 AppRouter 类型,VSCode 就不知道返回值结构
VSCode 补全 trpc.post.list.useQuery() 的返回数据类型,前提是 createTRPCReact(或 createTRPCProxyClient)明确知道泛型是哪个 router。
典型错误写法:const trpc = createTRPCReact<approuter>()</approuter> 写在了组件内部,或者类型引用路径出错,导致类型丢失。
- 必须从服务端统一导出
AppRouter,例如:export type { AppRouter } from '@/server/routers/_app' - 客户端初始化要显式标注:
const trpc = createTRPCReact<approuter>()</approuter>,不能靠类型推断 - 如果用了
createTRPCProxyClient,同样要写成createTRPCProxyClient<approuter>()</approuter> - 避免在
trpc/client.ts里 import * as trpc,这会切断类型链路
服务端 createCaller 返回类型没导出,server-side 调用没补全
在 Next.js API route 或 server actions 里调用 appRouter.createCaller({}) 时,如果返回类型没被导出,VSCode 就无法提示 caller.post.create({...}) 的参数和返回值。
问题根源在于:tRPC 默认不把 createCaller 的返回类型挂到 AppRouter 上,得手动暴露。
- 在
src/server/routers/_app.ts里加一行:export type ServerRouter = typeof appRouter - 然后在 caller 使用处:
const caller = appRouter.createCaller({}) as ServerRouter,或更稳妥地:const caller = appRouter.createCaller<context>({})</context> - 注意:
Context类型必须可静态分析,不能含动态 import 或条件类型 - 如果用了 tRPC v11 的
createCallerFactory,记得导出工厂返回的类型别名
最常被忽略的一点:tRPC 类型链路是“单向穿透”的——客户端类型依赖服务端 router 定义,但服务端 router 又依赖 zod schema 的静态可读性。只要中间任意一层用了 z.any()、z.custom() 或运行时拼接的 schema,整条链路的补全就会断裂。不是配置问题,是类型本身不可推导。


















