@tanstack/preact-query 无限查询预取的类型契约:`UsePrefetchInfiniteQueryOptions` 深度解析
tanstack/preact-query 无限查询预取的类型契约UsePrefetchInfiniteQueryOptions深度解析【免费下载链接】query Powerful asynchronous state management, server-state utilities and data fetching for the web. TS/JS, React Query, Solid Query, Svelte Query and Vue Query.项目地址: https://gitcode.com/GitHub_Trending/qu/queryUsePrefetchInfiniteQueryOptions是 TanStack Query 的 Preact 适配层tanstack/preact-query中为usePrefetchInfiniteQuery定义的选项类型它刻画了在渲染期预取无限查询infinite query时允许传入的全部配置。读完本文你将掌握该类型的完整声明结构、每个类型参数的职责与默认值、queryFn为何禁止skipToken以及它在渲染期预取与 Suspense 协作背后的运行时语义并能在自己的 Preact 组件里正确、类型安全地写出fetch-as-you-render式的分页预取代码。该类型在 preact-query 中的定位在 packages/preact-query/src/types.ts 中所有 hook 的选项类型都遵循一条设计主线UI 层类型 复用tanstack/query-core的命令式执行选项 剔除 / 收紧不适合 hook 场景的字段。UsePrefetchInfiniteQueryOptions正是为usePrefetchInfiniteQuery提供参数的类型普通查询的预取对应 UsePrefetchQueryOptions无限查询的预取则对应本主题UsePrefetchInfiniteQueryOptionspackages/preact-query/src/types.ts#L117。两者与 Suspense 类 hook如UseSuspenseInfiniteQueryOptions形成预取 → 渲染的成对关系共同支撑 Suspense 指南 所描述的渲染期预取 边界内消费模式。完整的类型声明与继承关系原文给出的核心声明如下定义于 packages/preact-query/src/types.ts:117type UsePrefetchInfiniteQueryOptionsTQueryFnData, TError, TData, TQueryKey, TPageParam DistributiveOmitInfiniteQueryExecuteOptionsTQueryFnData, TError, TData, TQueryKey, TPageParam, queryFn object;它表达的语义是usePrefetchInfiniteQuery接受的选项与queryClient.infiniteQuery完全一致——也就是说除了queryFn在未定义默认查询函数时必须显式提供之外其余所有字段都与命令式执行选项相同。结合底层源码可以拆解出整条继承链顶层使用DistributiveOmit..., queryFn剔除queryFn联合分发式剔除其定义见 packages/query-core/src/types.ts#L14TObject extends any ? OmitTObject, TKey : never。被剔除的基座InfiniteQueryExecuteOptions定义于 packages/query-core/src/types.ts#L572其结构为OmitQueryExecuteOptions..., InfiniteDataTQueryFnData, TPageParam, ..., initialPageParam InitialPageParamTPageParam InfiniteQueryPagesTQueryFnData, TPageParamQueryExecuteOptions见 packages/query-core/src/types.ts#L492在QueryOptions之上强制queryKey必填并提供select与staleTime。无限查询通过InitialPageParam与InfiniteQueryPages两个交叉类型叠加了分页语义initialPageParam必填作为首屏页码pagesgetNextPageParam是一组可选的批量抓取方式多页一次性预取时使用也可省略只抓首页。于是最终落到UsePrefetchInfiniteQueryOptions上的实际约束是queryKey、initialPageParam、getNextPageParam在未指定pages的逐页模式下的后续翻页依据都始终要求提供——这一点在 hook 的 JSDocpackages/preact-query/src/usePrefetchInfiniteQuery.tsx#L10中有明确说明也由类型测试用例should require initialPageParam and getNextPageParampackages/preact-query/src/tests/usePrefetchInfiniteQuery.test-d.tsx做了编译期验证。关键成员queryFn的特殊收紧optional queryFn: ExcludeInfiniteQueryExecuteOptionsTQueryFnData, TError, TData, TQueryKey, TPageParam[queryFn], SkipToken;queryFn是唯一被类型系统主动改造的字段规则有两层skipToken被排除在合法值之外。SkipToken是 query-core 导出的特殊占位符用于表达当前尚无可用参数请暂停查询例如在useQuery中用enabled类似的效果实现条件查询但预取场景里一次预取必须真的发起一次请求才能给后续 Suspense 消费方铺好数据因此在类型层面直接将其剔除。类型测试用例should not allow skipToken in queryFnusePrefetchInfiniteQuery.test-d.tsx同时验证了直接传skipToken与三元表达式下出现skipToken两种写法都会报类型错误。唯一的例外是定义了默认查询函数。如果你通过new QueryClient({ defaultOptions: { queries: { queryFn } } })配置了全局默认查询函数那么queryFn可以整体省略——预取会回落到默认实现。这也正是类型声明中该字段为optional可选而非必填的原因。与之相对的观察者observer式 hook 里常出现的enabled、refetchInterval、throwOnError这类与订阅、重渲染相关的选项在queryClient命令式执行的选项体系中本身就不存在预取是单次点火不是常驻观察所以也无法传给预取 hook。类型测试should not allow refetchInterval, enabled or throwOnError optionsusePrefetchInfiniteQuery.test-d.tsx逐一对这三个字段做了ts-expect-error校验说明这些字段在编译期就会被拒绝。五个类型参数逐一解读该类型把单页数据类型、错误类型、最终数据类型、缓存键类型、翻页参数类型拆成了 5 个独立维度方便类型推断与复用类型参数约束 / 默认值含义TQueryFnData unknown你的queryFn解析出的单个页面的数据类型。注意它是一页如Project[]或{ items, nextCursor }而非InfiniteData...包装后的整体。TError DefaultError你的queryFn可能抛出的错误类型默认是 query-core 的DefaultError。TData TQueryFnDataselect执行后data最终呈现的类型。此处默认值刻意设为单个页面原因是预取本身永远不会把data读回给你hook 返回void这个参数只在你把这组 options 复用到其他带select的地方时才真正影响类型结果。TQueryKeyextendsQueryKey默认QueryKey你的queryKey的类型泛型约束保证了它必须是 query-core 认可的缓存键形态数组。TPageParam unknown传给queryFn用于抓取某一页的参数类型即queryFn上下文里pageParam的类型。将它与 UseInfiniteQueryOptions普通useInfiniteQuery的选项类型对比能看出一个刻意为之的差异后者TData默认是InfiniteDataTQueryFnData所有已抓取页面页码的整体形状因为 hook 会把完整数据交还给你渲染而预取版本的TData默认退回单个页面因为UsePrefetchInfiniteQueryOptions从设计上就不需要描述读出来是什么只需保证传给queryClient.infiniteQuery时能被正确推断。运行时行为这个类型驱动的 hook 到底做了什么理解了选项类型再看它的消费者实现会更清晰。usePrefetchInfiniteQuery 的完整逻辑非常短export function usePrefetchInfiniteQueryTQueryFnData, TError, TData, TQueryKey extends QueryKey, TPageParam( options: UsePrefetchInfiniteQueryOptionsTQueryFnData, TError, TData, TQueryKey, TPageParam, queryClient?: QueryClient, ) { const client useQueryClient(queryClient) if (!client.getQueryState(options.queryKey)) { void client.infiniteQuery(options).catch(noop) } }几个值得注意的运行时事实渲染期点火hook 不返回任何值void纯粹为了在组件渲染、且位于某个包裹 Suspense 消费方的边界之前发起预取从而让useSuspenseInfiniteQuery挂起时数据已经在途或就绪。缓存守卫只有当client.getQueryState(options.queryKey)为空——即该缓存键从未存在任何状态时才发起预取如果已存在pending/error等状态则直接跳过。因此每次渲染都调用非常廉价不会重复抓取已有数据或已在途的数据。静默吞错通过.catch(noop)把预取失败吞掉避免在渲染阶段产生未处理的 Promise 拒绝。底层执行queryClient.infiniteQuery见 packages/query-core/src/queryClient.ts#L437会设置options._type infinite后走统一的query执行管线返回InfiniteData...或select之后的TData。运行时测试 usePrefetchInfiniteQuery.test.tsx 验证了这一整套流程在Page内部通过useSuspenseInfiniteQuery(queryOpts)消费同一个 query key 时预取在父组件渲染期便已把第一页数据写入缓存Suspense 挂起后随即以已抓取数据恢复渲染。实战示例类型参数如何被推断把上面的类型知识组合成一个典型用例。下面这组代码取自 hook JSDoc 的示例模式见 usePrefetchInfiniteQuery.tsx#L27 附近并加入了显式类型标注以展示 5 个参数如何落位import { Suspense } from preact/compat import { infiniteQueryOptions, usePrefetchInfiniteQuery, } from tanstack/preact-query interface ProjectPage { items: string[] nextId: number | undefined } const projectsOptions infiniteQueryOptions({ queryKey: [projects], // pageParam 的类型由 initialPageParam / getNextPageParam 推断为 number queryFn: ({ pageParam }) fetchProjects(pageParam), initialPageParam: 0, getNextPageParam: (lastPage) lastPage.nextId, }) function App() { // 在 Suspense 边界之前触发预取返回 void usePrefetchInfiniteQuery(projectsOptions) return ( Suspense fallback{h1Loading projects.../h1} Projects / /Suspense ) }这里TQueryFnData ProjectPage、TPageParam number由initialPageParam: 0推断TQueryKey [projects]对应的字面量数组类型TError、TData保持默认。若误把queryFn写成skipToken或试图传入enabled、refetchIntervalTypeScript 都会立即给出编译错误——这正是本类型提供的约束即文档价值。借助infiniteQueryOptions它会根据queryFn与initialPageParam反推出精确的TQueryFnData、TPageParamoptions 还能被同时复用到useSuspenseInfiniteQuery与预取 hook保证消费方拿到的data.pages形状与预取写入缓存的形状始终一致。使用注意与常见误区不要期待返回值预取选项里TData默认是单页而非InfiniteData正是因为结果不会回读。如果你在别处复用 options 时才需要关心select输出。skipToken在这里无效预取必须在渲染期真正发一次请求若请求参数尚不可知应当延迟渲染这个发起预取的组件本身而不是靠skipToken挂起。缓存命中即跳过利用这一守卫可以放心地在每次渲染调用而无需手动判断isFetching之类状态。第二参数queryClient类型签名允许传入自定义QueryClient否则从最近的 Provider 上下文取用适合在测试或隔离场景中注入独立实例。相关源码路径速查类型定义本体packages/preact-query/src/types.ts#L117基座类型InfiniteQueryExecuteOptions与DistributiveOmitpackages/query-core/src/types.ts#L572、packages/query-core/src/types.ts#L14消费该类型的 hook 实现packages/preact-query/src/usePrefetchInfiniteQuery.tsx底层命令式执行queryClient.infiniteQuerypackages/query-core/src/queryClient.ts#L437类型级测试参数约束、skipToken拒绝、无返回值packages/preact-query/src/tests/usePrefetchInfiniteQuery.test-d.tsx运行时测试渲染期预取 Suspense 消费packages/preact-query/src/tests/usePrefetchInfiniteQuery.test.tsx同文件的成对类型普通查询预取、Suspense 无限查询选项packages/preact-query/src/types.ts#L79、packages/preact-query/src/types.ts#L279【免费下载链接】query Powerful asynchronous state management, server-state utilities and data fetching for the web. TS/JS, React Query, Solid Query, Svelte Query and Vue Query.项目地址: https://gitcode.com/GitHub_Trending/qu/query创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考