You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何正确传递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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.18 01:40:24