如何正确传递tRPC v10查询过程至组件并完成类型定义
为支持无限查询的tRPC Procedure定义正确类型
你碰到的问题在于QueryProcedure<any>类型不包含useInfiniteQuery方法——tRPC会根据procedure的输入是否包含cursor、输出是否包含nextCursor自动推断它是否支持无限查询,对应的专属类型是InfiniteQueryProcedure。下面是具体的解决步骤:
1. 导入tRPC相关类型
首先确保引入tRPC的类型定义:
import type { InfiniteQueryProcedure } from '@trpc/server'; // 导入你的表格组件类型,比如antd的ColumnsType import type { ColumnsType } from 'antd/es/table';
2. 定义约束无限查询的类型结构
先统一无限查询的输出格式(必须包含items和nextCursor),再给组件Props做类型约束:
// 约定无限查询的返回结构 type InfiniteQueryResult<TData> = { items: TData[]; nextCursor?: number | string | null; }; // 组件Props类型:约束dataQuery是支持无限查询的tRPC procedure type Props<T extends object> = { columns: ColumnsType<T>; defaultSortField: string; dataQuery: InfiniteQueryProcedure< // 输入参数至少包含limit,cursor可选(适配首次查询) { limit: number; cursor?: number | string | null }, unknown, // 忽略ctx类型,不影响组件使用 InfiniteQueryResult<T> // 输出必须符合约定的结构 >; };
3. 调整组件实现
现在组件可以安全调用useInfiniteQuery,类型推导会完全生效:
const CustomTable = <T extends object>(props: Props<T>) => { const res = props.dataQuery.useInfiniteQuery({ limit: 10, // 若需要默认排序,可结合defaultSortField传入,比如: // [props.defaultSortField]: 'asc' }); // 后续可以安全访问分页数据,比如合并所有页的items: const tableData = res.data?.pages.flatMap(page => page.items) ?? []; return ( // 你的表格渲染逻辑,用tableData作为数据源 ); };
4. 优化tRPC路由(可选但推荐)
把路由里的cursor改为可选类型,适配首次查询不需要传入cursor的场景:
const fooRouter = t.router({ getFoos: t.procedure .input( z.object({ limit: z.number().min(1).max(100), cursor: z.number().optional(), // 改为可选 }), ) .query(async ({ ctx, input }) => { const { limit, cursor } = input; const queryRes = await ctx.prisma.foo.findMany({ take: limit + 1, // 只有cursor存在时才传入cursor参数 ...(cursor && { cursor: { Id: cursor } }), }); let nextCursor: typeof cursor; if (queryRes.length > limit) { const nextRes = queryRes.pop(); nextCursor = nextRes?.Id; } return { items: queryRes, nextCursor, }; }), });
更灵活的泛型扩展(可选)
如果需要支持不同的输入参数(比如自定义排序、筛选条件),可以把输入类型也作为泛型参数:
type Props<T extends object, TInput extends { limit: number; cursor?: any } = { limit: number; cursor?: number }> = { columns: ColumnsType<T>; defaultSortField: string; dataQuery: InfiniteQueryProcedure<TInput, unknown, InfiniteQueryResult<T>>; }; const CustomTable = <T extends object, TInput extends { limit: number; cursor?: any }>(props: Props<T, TInput>) => { const res = props.dataQuery.useInfiniteQuery({ limit: 10, [props.defaultSortField]: 'asc', // 这里可以传入符合TInput类型的其他参数 }); // ... };
内容的提问来源于stack exchange,提问作者samvdst
相关产品推荐
相关产品推荐

