NextJS tRPC TypeScript类型错误求助:void/undefined不兼容
TRPC + TypeScript 类型错误排查与解决
问题描述
开启TypeScript严格模式时,本地VS Code无报错,但部署到Vercel时构建失败;关闭严格模式后,VS Code出现两类明确的类型错误:
- 调用
trpc.useQuery传入id参数时:Type '{ id: number; }' is not assignable to type 'undefined' - 调用
mutateAsync传入countdownId时:Argument of type '{ countdownId: number; }' is not assignable to parameter of type 'void'
代码示例
前端代码(admin.tsx)
import { trpc } from "../../utils/trpc"; function Admin(props: {id: number}) { // 错误1:Type '{ id: number; }' is not assignable to type 'undefined' const { data: countdown, isLoading } = trpc.useQuery([ "countdown.getCountdownAdmin", { id: props.id }, ]); const utils = trpc.useContext(); // 关联错误2的mutation定义 const mutateResults = trpc.useMutation(["countdown.updateResults"], { async onSuccess() { await utils.invalidateQueries(["countdown.getCountdownAdmin"]); }, }); // 错误2:Argument of type '{ countdownId: number; }' is not assignable to parameter of type 'void' async function generateResults() { const data = await mutateResults.mutateAsync({ countdownId: countdown.id, }); } return ( {/* 无关代码省略 */} ); } export default Admin;
服务端代码(countdown.ts)
import { TRPCError } from "@trpc/server"; import { createProtectedRouter } from "./protected-router"; import { z } from "zod"; export const countdownProtected = createProtectedRouter() .query("getCountdownAdmin", { input: z.object({ id: z.number() }), async resolve({ ctx, input }) { const { id } = input; try { const countdown = await ctx.prisma.countdowns.findUnique({ where: { id }, include: { UserCountdowns: { include: { User: { include: { Votes: { where: { countdownId: id } } } }, }, }, Results: true, InviteCode: true, }, }); return countdown; } catch (e) { console.log(e); throw new TRPCError({ code: "BAD_REQUEST" }); } }, }) .mutation("updateResults", { input: z.object({ countdownId: z.number() }), async resolve({ ctx, input }) { const { countdownId } = input; try { const data = await ctx.prisma.countdowns.findUnique({ where: { id: countdownId }, include: { Votes: { include: { User: true }, orderBy: { id: "asc" } } }, }); // 业务逻辑省略 } catch (e) { console.log(e); throw new TRPCError({ code: "BAD_REQUEST" }); } }, });
解决方案
1. 修复TRPC客户端类型不匹配问题
两类错误的核心原因都是客户端未正确导入服务端的Router类型,导致TRPC无法自动推导query/mutation的输入输出类型。
步骤:
- 在客户端的TRPC工具文件(如
utils/trpc.ts)中,确保导入服务端的根Router类型,并创建带类型的TRPC客户端:import { createTRPCReact } from "@trpc/react-query"; // 替换为你服务端根Router的实际路径 import type { AppRouter } from "../server/routers/_app"; export const trpc = createTRPCReact<AppRouter>(); - 重新编译服务端代码,确保类型定义正确生成;若使用了TRPC的代码生成工具,执行生成命令更新类型。
- 重启VS Code的TypeScript服务(快捷键
Ctrl+Shift+P,选择TypeScript: Restart TS Server),清除类型缓存。
2. 处理countdown的undefined风险
useQuery返回的data在加载阶段为undefined,直接访问countdown.id会触发潜在的运行时错误,同时也会影响类型推导:
async function generateResults() { if (!countdown) return; // 先判断countdown是否存在,避免undefined访问 const data = await mutateResults.mutateAsync({ countdownId: countdown.id, }); }
3. 解决严格模式下本地与构建环境的差异
严格模式下Vercel构建失败但本地无报错,通常是因为:
- 本地VS Code使用的TS版本与项目依赖的TS版本不一致:点击VS Code右下角状态栏的TS版本,选择
Use Workspace Version。 - 本地TS缓存未更新:删除
node_modules/.cache目录,或重启TS服务。 - 确保
tsconfig.json的严格模式配置在本地和Vercel一致,比如设置"strict": true,避免环境差异导致的类型检查结果不同。
内容的提问来源于stack exchange,提问作者Karumu
相关产品推荐
相关产品推荐

