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

React Query useInfiniteQuery类型不匹配导致npm run build失败

解决React Query无限滚动的类型不匹配问题

问题概述

用React Query结合react-intersection-observer实现无限滚动时,开发环境代码运行正常,但执行npm run build触发TypeScript类型不匹配错误。核心矛盾是getNextPageParam返回字符串类型的文档ID,而getInfinitePosts的pageParam被定义为number类型;尝试将pageParam改为字符串类型后,类型错误消失,但无限滚动功能失效。

错误提示

No overload matches this call.
No overload matches this call.
 Overload 1 of 3, '(options: UndefinedInitialDataInfiniteOptions<DocumentList<Document> | undefined, Error, InfiniteData<DocumentList<Document> | undefined, unknown>, QUERY_KEYS[], number>, queryClient?: QueryClient | undefined): UseInfiniteQueryResult<...>', gave the following error.
   Type '(lastPage: DocumentList<Document> | undefined) => string | null' is not assignable to type 'GetNextPageParamFunction<number, DocumentList<Document> | undefined>'.
     Type 'string | null' is not assignable to type 'number | null | undefined'.
       Type 'string' is not assignable to type 'number'.
 Overload 2 of 3, '(options: DefinedInitialDataInfiniteOptions<DocumentList<Document> | undefined, Error, InfiniteData<DocumentList<Document> | undefined, unknown>, QUERY_KEYS[], number>, queryClient?: QueryClient | undefined): DefinedUseInfiniteQueryResult<...>', gave the following error.
   Type '(lastPage: DocumentList<Document> | undefined) => string | null' is not assignable to type 'GetNextPageParamFunction<number, DocumentList<Document> | undefined>'.
 Overload 3 of 3, '(options: UseInfiniteQueryOptions<DocumentList<Document> | undefined, Error, InfiniteData<DocumentList<Document> | undefined, unknown>, DocumentList<...> | undefined, QUERY_KEYS[], number>, queryClient?: QueryClient | undefined): UseInfiniteQueryResult<...>', gave the following error.
   Type '(lastPage: DocumentList<Document> | undefined) => string | null' is not assignable to type 'GetNextPageParamFunction<number, DocumentList<Document> | undefined>'.ts(2769)
queryClient-b7fce2ff.d.ts(335, 5): The expected type comes from property 'getNextPageParam' which is declared here on type 'UndefinedInitialDataInfiniteOptions<DocumentList<Document> | undefined, Error, InfiniteData<DocumentList<Document> | undefined, unknown>, QUERY_KEYS[], number>'
queryClient-b7fce2ff.d.ts(335, 5): The expected type comes from property 'getNextPageParam' which is declared here on type 'DefinedInitialDataInfiniteOptions<DocumentList<Document> | undefined, Error, InfiniteData<DocumentList<Document> | undefined, unknown>, QUERY_KEYS[], number>'
queryClient-b7fce2ff.d.ts(335, 5): The expected type comes from property 'getNextPageParam' which is declared here on type 'UseInfiniteQueryOptions<DocumentList<Document> | undefined, Error, InfiniteData<DocumentList<Document> | undefined, unknown>, DocumentList<...> | undefined, QUERY_KEYS[], number>'
(property) getNextPageParam: GetNextPageParamFunction<number, Models.DocumentList<Models.Document> | undefined>
This function can be set to automatically get the next cursor for infinite queries. The result will also be used to determine the value of hasNextPage.

原代码片段

自定义Hook:useGetPosts

export const useGetPosts = () => {
  return useInfiniteQuery({
    queryKey: [QUERY_KEYS.GET_INFINITE_POSTS],
    queryFn: getInfinitePosts,
    getNextPageParam: (lastPage) => {
      if (lastPage && lastPage.documents.length === 0) {
        return null;
      }

      const lastId = lastPage.documents[lastPage.documents.length - 1].$id;
      return lastId;
    },
  });
};

数据请求函数:getInfinitePosts

export async function getInfinitePosts({ pageParam }: { pageParam: number }) {
  const queries = [Query.orderDesc('$updatedAt'), Query.limit(12)];

  if (pageParam !== undefined) {
    queries.push(Query.cursorAfter(pageParam.toString()));
  }

  try {
    const posts = await databases.listDocuments(
      appwriteConfig.databaseId,
      appwriteConfig.postsCollectionId,
      queries
    );

    if (!posts) throw Error;

    return posts;
  } catch (error) {
    console.log(error);
  }
}

组件调用代码

const { ref, inView } = useInView();
const { data: posts, fetchNextPage, hasNextPage } = useGetPosts();

const [searchValue, setSearchValue] = useState('');
const debouncedSearch = useDebounce(searchValue, 500);
const { data: searchedPosts, isFetching: isSearchFetching } = useSearchPosts(debouncedSearch);

useEffect(() => {
  if (inView && !searchValue) {
    fetchNextPage();
  }
}, [inView, searchValue]);

解决方案

1. 统一参数类型为字符串

将getInfinitePosts的pageParam类型改为可选字符串,同时去掉不必要的类型转换:

export async function getInfinitePosts({ pageParam }: { pageParam?: string }) {
  const queries = [Query.orderDesc('$updatedAt'), Query.limit(12)];

  // 直接使用pageParam,无需转换
  if (pageParam) {
    queries.push(Query.cursorAfter(pageParam));
  }

  try {
    const posts = await databases.listDocuments(
      appwriteConfig.databaseId,
      appwriteConfig.postsCollectionId,
      queries
    );

    if (!posts) throw new Error('Failed to fetch posts');

    return posts;
  } catch (error) {
    console.error('Error fetching posts:', error);
    // 必须抛出错误,让React Query捕获加载失败状态
    throw error;
  }
}

2. 显式指定React Query泛型类型

给useInfiniteQuery指定完整泛型参数,确保TypeScript正确推断pageParam的类型为字符串:

import { UseInfiniteQueryOptions, useInfiniteQuery } from '@tanstack/react-query';
// 替换为你项目中的实际类型导入
import { DocumentList, Document } from './models';

export const useGetPosts = () => {
  return useInfiniteQuery<DocumentList<Document>, Error, DocumentList<Document>, typeof QUERY_KEYS[], string>({
    queryKey: [QUERY_KEYS.GET_INFINITE_POSTS],
    queryFn: getInfinitePosts,
    getNextPageParam: (lastPage) => {
      if (!lastPage || lastPage.documents.length === 0) {
        return null;
      }

      const lastId = lastPage.documents[lastPage.documents.length - 1].$id;
      return lastId;
    },
    // 显式设置初始参数为undefined,符合string | undefined类型
    initialPageParam: undefined,
  });
};

关键说明

之前修改字符串类型后无限滚动失效,大概率是两个原因:

  • 原getInfinitePosts中没有抛出错误,React Query无法感知请求失败,导致hasNextPage状态异常
  • 未显式指定泛型,TypeScript自动推断错误,导致pageParam传递异常

来源标注

内容的提问来源于stack exchange,提问作者Bogdan Titsky

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.02 10:17:32