From 01a81eec6ec87ad1e5b30b547b12c91487dadd6b Mon Sep 17 00:00:00 2001 From: TkDodo Date: Fri, 14 Feb 2025 15:24:02 +0100 Subject: [PATCH 01/25] feat: imperative infinite queries let's bring back imperative infinite queries, but only allow them in an all-or-nothing mode, dependent on if `getNextPageParam` has been passed --- .../__tests__/infiniteQueryBehavior.test.tsx | 104 ++++++++++++++++++ .../query-core/src/infiniteQueryBehavior.ts | 17 +-- .../query-core/src/infiniteQueryObserver.ts | 19 ++-- packages/query-core/src/query.ts | 2 +- packages/query-core/src/types.ts | 2 +- 5 files changed, 127 insertions(+), 17 deletions(-) diff --git a/packages/query-core/src/__tests__/infiniteQueryBehavior.test.tsx b/packages/query-core/src/__tests__/infiniteQueryBehavior.test.tsx index 8862b368a43..3f4ab1e8b2c 100644 --- a/packages/query-core/src/__tests__/infiniteQueryBehavior.test.tsx +++ b/packages/query-core/src/__tests__/infiniteQueryBehavior.test.tsx @@ -201,6 +201,110 @@ describe('InfiniteQueryBehavior', () => { unsubscribe() }) + test('InfiniteQueryBehavior should apply pageParam', async () => { + const key = queryKey() + + const queryFn = vi.fn().mockImplementation(({ pageParam }) => { + return pageParam + }) + + const observer = new InfiniteQueryObserver(queryClient, { + queryKey: key, + queryFn, + initialPageParam: 0, + }) + + let observerResult: + | InfiniteQueryObserverResult + | undefined + + const unsubscribe = observer.subscribe((result) => { + observerResult = result + }) + + // Wait for the first page to be fetched + await waitFor(() => + expect(observerResult).toMatchObject({ + isFetching: false, + data: { pages: [0], pageParams: [0] }, + }), + ) + + queryFn.mockClear() + + // Fetch the next page using pageParam + await observer.fetchNextPage({ pageParam: 1 }) + + expect(queryFn).toHaveBeenNthCalledWith(1, { + queryKey: key, + pageParam: 1, + meta: undefined, + client: queryClient, + direction: 'forward', + signal: expect.anything(), + }) + + expect(observerResult).toMatchObject({ + isFetching: false, + data: { pages: [0, 1], pageParams: [0, 1] }, + }) + + queryFn.mockClear() + + // Fetch the previous page using pageParam + await observer.fetchPreviousPage({ pageParam: -1 }) + + expect(queryFn).toHaveBeenNthCalledWith(1, { + queryKey: key, + pageParam: -1, + meta: undefined, + client: queryClient, + direction: 'backward', + signal: expect.anything(), + }) + + expect(observerResult).toMatchObject({ + isFetching: false, + data: { pages: [-1, 0, 1], pageParams: [-1, 0, 1] }, + }) + + queryFn.mockClear() + + // Refetch pages: old manual page params should be used + await observer.refetch() + + expect(queryFn).toHaveBeenCalledTimes(3) + + expect(queryFn).toHaveBeenNthCalledWith(1, { + queryKey: key, + pageParam: -1, + meta: undefined, + client: queryClient, + direction: 'forward', + signal: expect.anything(), + }) + + expect(queryFn).toHaveBeenNthCalledWith(2, { + queryKey: key, + pageParam: 0, + meta: undefined, + client: queryClient, + direction: 'forward', + signal: expect.anything(), + }) + + expect(queryFn).toHaveBeenNthCalledWith(3, { + queryKey: key, + pageParam: 1, + meta: undefined, + client: queryClient, + direction: 'forward', + signal: expect.anything(), + }) + + unsubscribe() + }) + test('InfiniteQueryBehavior should support query cancellation', async () => { const key = queryKey() let abortSignal: AbortSignal | null = null diff --git a/packages/query-core/src/infiniteQueryBehavior.ts b/packages/query-core/src/infiniteQueryBehavior.ts index 9cb616da273..0d978e1f5ae 100644 --- a/packages/query-core/src/infiniteQueryBehavior.ts +++ b/packages/query-core/src/infiniteQueryBehavior.ts @@ -14,7 +14,7 @@ export function infiniteQueryBehavior( return { onFetch: (context, query) => { const options = context.options as InfiniteQueryPageParamsOptions - const direction = context.fetchOptions?.meta?.fetchMore?.direction + const fetchMore = context.fetchOptions?.meta?.fetchMore const oldPages = context.state.data?.pages || [] const oldPageParams = context.state.data?.pageParams || [] let result: InfiniteData = { pages: [], pageParams: [] } @@ -81,14 +81,17 @@ export function infiniteQueryBehavior( } // fetch next / previous page? - if (direction && oldPages.length) { - const previous = direction === 'backward' + if (fetchMore && oldPages.length) { + const previous = fetchMore.direction === 'backward' const pageParamFn = previous ? getPreviousPageParam : getNextPageParam const oldData = { pages: oldPages, pageParams: oldPageParams, } - const param = pageParamFn(options, oldData) + const param = + fetchMore.pageParam === undefined + ? pageParamFn(options, oldData) + : fetchMore.pageParam result = await fetchPage(oldData, param, previous) } else { @@ -97,8 +100,8 @@ export function infiniteQueryBehavior( // Fetch all pages do { const param = - currentPage === 0 - ? (oldPageParams[0] ?? options.initialPageParam) + currentPage === 0 || !options.getNextPageParam + ? (oldPageParams[currentPage] ?? options.initialPageParam) : getNextPageParam(options, result) if (currentPage > 0 && param == null) { break @@ -136,7 +139,7 @@ function getNextPageParam( ): unknown | undefined { const lastIndex = pages.length - 1 return pages.length > 0 - ? options.getNextPageParam( + ? options.getNextPageParam?.( pages[lastIndex], pages, pageParams[lastIndex], diff --git a/packages/query-core/src/infiniteQueryObserver.ts b/packages/query-core/src/infiniteQueryObserver.ts index b1c18ac01c1..c898ab9c3f9 100644 --- a/packages/query-core/src/infiniteQueryObserver.ts +++ b/packages/query-core/src/infiniteQueryObserver.ts @@ -124,24 +124,27 @@ export class InfiniteQueryObserver< > } - fetchNextPage( - options?: FetchNextPageOptions, - ): Promise> { + fetchNextPage({ pageParam, ...options }: FetchNextPageOptions = {}): Promise< + InfiniteQueryObserverResult + > { return this.fetch({ ...options, meta: { - fetchMore: { direction: 'forward' }, + fetchMore: { direction: 'forward', pageParam }, }, }) } - fetchPreviousPage( - options?: FetchPreviousPageOptions, - ): Promise> { + fetchPreviousPage({ + pageParam, + ...options + }: FetchPreviousPageOptions = {}): Promise< + InfiniteQueryObserverResult + > { return this.fetch({ ...options, meta: { - fetchMore: { direction: 'backward' }, + fetchMore: { direction: 'backward', pageParam }, }, }) } diff --git a/packages/query-core/src/query.ts b/packages/query-core/src/query.ts index e6ad4ca4eea..c2b8ec19687 100644 --- a/packages/query-core/src/query.ts +++ b/packages/query-core/src/query.ts @@ -89,7 +89,7 @@ export interface QueryBehavior< export type FetchDirection = 'forward' | 'backward' export interface FetchMeta { - fetchMore?: { direction: FetchDirection } + fetchMore?: { direction: FetchDirection; pageParam?: unknown } } export interface FetchOptions { diff --git a/packages/query-core/src/types.ts b/packages/query-core/src/types.ts index 6d94daabc07..d52f56bc9b7 100644 --- a/packages/query-core/src/types.ts +++ b/packages/query-core/src/types.ts @@ -271,7 +271,7 @@ export interface InfiniteQueryPageParamsOptions< * 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`. */ - getNextPageParam: GetNextPageParamFunction + getNextPageParam?: GetNextPageParamFunction } export type ThrowOnError< From 644677eab7d572ef062324734c19d78bbd85a83b Mon Sep 17 00:00:00 2001 From: TkDodo Date: Fri, 14 Feb 2025 15:42:02 +0100 Subject: [PATCH 02/25] test: add type tests of expected behaviour (currently failing) --- .../infiniteQueryObserver.test-d.tsx | 43 +++++++++++++++++++ 1 file changed, 43 insertions(+) diff --git a/packages/query-core/src/__tests__/infiniteQueryObserver.test-d.tsx b/packages/query-core/src/__tests__/infiniteQueryObserver.test-d.tsx index f84c96067ad..7a3f81d91c1 100644 --- a/packages/query-core/src/__tests__/infiniteQueryObserver.test-d.tsx +++ b/packages/query-core/src/__tests__/infiniteQueryObserver.test-d.tsx @@ -72,4 +72,47 @@ describe('InfiniteQueryObserver', () => { expectTypeOf(result.status).toEqualTypeOf<'success'>() } }) + + it('should not allow pageParam on fetchNextPage / fetchPreviousPage if getNextPageParam is defined', async () => { + const observer = new InfiniteQueryObserver(queryClient, { + queryKey: queryKey(), + queryFn: ({ pageParam }) => String(pageParam), + initialPageParam: 1, + getNextPageParam: (page) => Number(page) + 1, + }) + + expectTypeOf() + .parameter(0) + .toEqualTypeOf< + { cancelRefetch?: boolean; throwOnError?: boolean } | undefined + >() + + expectTypeOf() + .parameter(0) + .toEqualTypeOf< + { cancelRefetch?: boolean; throwOnError?: boolean } | undefined + >() + }) + + it('should require pageParam on fetchNextPage / fetchPreviousPage if getNextPageParam is missing', async () => { + const observer = new InfiniteQueryObserver(queryClient, { + queryKey: queryKey(), + queryFn: ({ pageParam }) => String(pageParam), + initialPageParam: 1, + }) + + expectTypeOf() + .parameter(0) + .toEqualTypeOf< + | { pageParam: number; cancelRefetch?: boolean; throwOnError?: boolean } + | undefined + >() + + expectTypeOf() + .parameter(0) + .toEqualTypeOf< + | { pageParam: number; cancelRefetch?: boolean; throwOnError?: boolean } + | undefined + >() + }) }) From ec75834a28c2c29b9b4a5570236dfeaa19ee335f Mon Sep 17 00:00:00 2001 From: TkDodo Date: Fri, 3 Apr 2026 15:41:49 +0200 Subject: [PATCH 03/25] InfiniteQueryMode: 'imperative' --- .../infinite-query-options.test-d.ts | 36 +++- .../__tests__/inject-infinite-query.test-d.ts | 19 ++ .../angular-query-experimental/src/types.ts | 7 +- packages/preact-query/src/types.ts | 12 +- .../__tests__/infiniteQueryBehavior.test.tsx | 24 ++- .../infiniteQueryObserver.test-d.tsx | 93 ++++++--- .../src/__tests__/queryClient.test-d.tsx | 59 ++++-- .../src/__tests__/queryClient.test.tsx | 16 +- .../query-core/src/infiniteQueryBehavior.ts | 48 +++-- .../query-core/src/infiniteQueryObserver.ts | 181 ++++++++++++++---- packages/query-core/src/queryClient.ts | 117 ++++++++++- packages/query-core/src/types.ts | 166 ++++++++++++---- .../__tests__/infiniteQueryOptions.test-d.tsx | 38 +++- .../react-query/src/__tests__/ssr.test.tsx | 1 + .../src/__tests__/useInfiniteQuery.test-d.tsx | 20 ++ .../react-query/src/infiniteQueryOptions.ts | 43 ++++- packages/react-query/src/types.ts | 27 ++- packages/react-query/src/useInfiniteQuery.ts | 13 +- 18 files changed, 745 insertions(+), 175 deletions(-) diff --git a/packages/angular-query-experimental/src/__tests__/infinite-query-options.test-d.ts b/packages/angular-query-experimental/src/__tests__/infinite-query-options.test-d.ts index 4a7ce532bc2..0d04fc8ce6a 100644 --- a/packages/angular-query-experimental/src/__tests__/infinite-query-options.test-d.ts +++ b/packages/angular-query-experimental/src/__tests__/infinite-query-options.test-d.ts @@ -38,24 +38,27 @@ describe('infiniteQueryOptions', () => { const options = infiniteQueryOptions({ queryKey: ['key'], queryFn: () => Promise.resolve('string'), - getNextPageParam: () => 1, initialPageParam: 1, + mode: 'imperative', }) - const { data } = injectInfiniteQuery(() => options) + const { data, fetchNextPage } = injectInfiniteQuery(() => options) // known issue: type of pageParams is unknown when returned from useInfiniteQuery expectTypeOf(data()).toEqualTypeOf< InfiniteData | undefined >() + expectTypeOf(fetchNextPage).parameters.toEqualTypeOf< + [options: { pageParam: number; cancelRefetch?: boolean; throwOnError?: boolean }] + >() }) it('should work when passed to fetchInfiniteQuery', async () => { const options = infiniteQueryOptions({ queryKey: ['key'], queryFn: () => Promise.resolve('string'), - getNextPageParam: () => 1, initialPageParam: 1, + mode: 'imperative', }) const data = await new QueryClient().fetchInfiniteQuery(options) @@ -155,6 +158,33 @@ describe('infiniteQueryOptions', () => { ) }) + it('should reject missing mode / getNextPageParam and reject getters in imperative mode', () => { + // @ts-expect-error getNextPageParam is required unless mode is imperative + infiniteQueryOptions({ + queryKey: ['key'], + queryFn: () => Promise.resolve('string'), + initialPageParam: 1, + }) + + infiniteQueryOptions({ + queryKey: ['key'], + queryFn: () => Promise.resolve('string'), + initialPageParam: 1, + mode: 'imperative', + // @ts-expect-error getNextPageParam is not allowed in imperative mode + getNextPageParam: () => 1, + }) + + infiniteQueryOptions({ + queryKey: ['key'], + queryFn: () => Promise.resolve('string'), + initialPageParam: 1, + mode: 'imperative', + // @ts-expect-error getPreviousPageParam is not allowed in imperative mode + getPreviousPageParam: () => 0, + }) + }) + test('allow optional initialData function', () => { const initialData: { example: boolean } | undefined = { example: true } const queryOptions = infiniteQueryOptions({ diff --git a/packages/angular-query-experimental/src/__tests__/inject-infinite-query.test-d.ts b/packages/angular-query-experimental/src/__tests__/inject-infinite-query.test-d.ts index 7ec133adfb1..7808154411a 100644 --- a/packages/angular-query-experimental/src/__tests__/inject-infinite-query.test-d.ts +++ b/packages/angular-query-experimental/src/__tests__/inject-infinite-query.test-d.ts @@ -39,4 +39,23 @@ describe('injectInfiniteQuery', () => { expectTypeOf(data).toEqualTypeOf>() } }) + + test('should require pageParam on imperative fetch methods', () => { + const query = TestBed.runInInjectionContext(() => { + return injectInfiniteQuery(() => ({ + queryKey: ['infiniteQuery'], + queryFn: ({ pageParam }) => 'data on page ' + pageParam, + initialPageParam: 0, + mode: 'imperative', + })) + }) + + expectTypeOf(query.fetchNextPage).parameters.toEqualTypeOf< + [options: { pageParam: number; cancelRefetch?: boolean; throwOnError?: boolean }] + >() + + expectTypeOf(query.fetchPreviousPage).parameters.toEqualTypeOf< + [options: { pageParam: number; cancelRefetch?: boolean; throwOnError?: boolean }] + >() + }) }) diff --git a/packages/angular-query-experimental/src/types.ts b/packages/angular-query-experimental/src/types.ts index d71bec248f7..f8eec735c49 100644 --- a/packages/angular-query-experimental/src/types.ts +++ b/packages/angular-query-experimental/src/types.ts @@ -4,6 +4,7 @@ import type { DefaultError, DefinedInfiniteQueryObserverResult, DefinedQueryObserverResult, + DistributiveOmit, InfiniteQueryObserverOptions, InfiniteQueryObserverResult, MutateFunction, @@ -72,13 +73,13 @@ export interface BaseQueryNarrowing { > } -export interface CreateInfiniteQueryOptions< +export type CreateInfiniteQueryOptions< TQueryFnData = unknown, TError = DefaultError, TData = TQueryFnData, TQueryKey extends QueryKey = QueryKey, TPageParam = unknown, -> extends OmitKeyof< +> = DistributiveOmit< InfiniteQueryObserverOptions< TQueryFnData, TError, @@ -87,7 +88,7 @@ export interface CreateInfiniteQueryOptions< TPageParam >, 'suspense' -> {} +> export type CreateBaseQueryResult< TData = unknown, diff --git a/packages/preact-query/src/types.ts b/packages/preact-query/src/types.ts index c995a28b08e..7987211aaa3 100644 --- a/packages/preact-query/src/types.ts +++ b/packages/preact-query/src/types.ts @@ -99,13 +99,13 @@ export type AnyUseInfiniteQueryOptions = UseInfiniteQueryOptions< any, any > -export interface UseInfiniteQueryOptions< +export type UseInfiniteQueryOptions< TQueryFnData = unknown, TError = DefaultError, TData = TQueryFnData, TQueryKey extends QueryKey = QueryKey, TPageParam = unknown, -> extends OmitKeyof< +> = DistributiveOmit< InfiniteQueryObserverOptions< TQueryFnData, TError, @@ -114,7 +114,7 @@ export interface UseInfiniteQueryOptions< TPageParam >, 'suspense' -> { +> & { /** * Set this to `false` to unsubscribe this observer from updates to the query cache. * Defaults to `true`. @@ -124,16 +124,16 @@ export interface UseInfiniteQueryOptions< export type AnyUseSuspenseInfiniteQueryOptions = UseSuspenseInfiniteQueryOptions -export interface UseSuspenseInfiniteQueryOptions< +export type UseSuspenseInfiniteQueryOptions< TQueryFnData = unknown, TError = DefaultError, TData = TQueryFnData, TQueryKey extends QueryKey = QueryKey, TPageParam = unknown, -> extends OmitKeyof< +> = DistributiveOmit< UseInfiniteQueryOptions, 'queryFn' | 'enabled' | 'throwOnError' | 'placeholderData' -> { +> & { queryFn?: Exclude< UseInfiniteQueryOptions< TQueryFnData, diff --git a/packages/query-core/src/__tests__/infiniteQueryBehavior.test.tsx b/packages/query-core/src/__tests__/infiniteQueryBehavior.test.tsx index 2cb9f0a2d53..79408e21b49 100644 --- a/packages/query-core/src/__tests__/infiniteQueryBehavior.test.tsx +++ b/packages/query-core/src/__tests__/infiniteQueryBehavior.test.tsx @@ -66,7 +66,12 @@ describe('InfiniteQueryBehavior', () => { }) let observerResult: - | InfiniteQueryObserverResult + | InfiniteQueryObserverResult< + InfiniteData, + Error, + number, + 'imperative' + > | undefined const unsubscribe = observer.subscribe((result) => { @@ -202,14 +207,27 @@ describe('InfiniteQueryBehavior', () => { return pageParam }) - const observer = new InfiniteQueryObserver(queryClient, { + const observer = new InfiniteQueryObserver< + number, + Error, + InfiniteData, + typeof key, + number, + 'imperative' + >(queryClient, { queryKey: key, queryFn, + mode: 'imperative', initialPageParam: 0, }) let observerResult: - | InfiniteQueryObserverResult + | InfiniteQueryObserverResult< + InfiniteData, + Error, + number, + 'imperative' + > | undefined const unsubscribe = observer.subscribe((result) => { diff --git a/packages/query-core/src/__tests__/infiniteQueryObserver.test-d.tsx b/packages/query-core/src/__tests__/infiniteQueryObserver.test-d.tsx index 73a2633ad4a..90b8d110ac0 100644 --- a/packages/query-core/src/__tests__/infiniteQueryObserver.test-d.tsx +++ b/packages/query-core/src/__tests__/infiniteQueryObserver.test-d.tsx @@ -81,38 +81,81 @@ describe('InfiniteQueryObserver', () => { getNextPageParam: (page) => Number(page) + 1, }) - expectTypeOf() - .parameter(0) - .toEqualTypeOf< - { cancelRefetch?: boolean; throwOnError?: boolean } | undefined - >() - - expectTypeOf() - .parameter(0) - .toEqualTypeOf< - { cancelRefetch?: boolean; throwOnError?: boolean } | undefined - >() + observer.fetchNextPage() + observer.fetchPreviousPage({ cancelRefetch: false }) + + // @ts-expect-error pageParam is not allowed in declarative mode + observer.fetchNextPage({ pageParam: 2 }) + + // @ts-expect-error pageParam is not allowed in declarative mode + observer.fetchPreviousPage({ pageParam: 0 }) }) it('should require pageParam on fetchNextPage / fetchPreviousPage if getNextPageParam is missing', async () => { - const observer = new InfiniteQueryObserver(queryClient, { + const observer = new InfiniteQueryObserver< + string, + Error, + InfiniteData, + ReturnType, + number, + 'imperative' + >(queryClient, { + queryKey: queryKey(), + queryFn: ({ pageParam }) => String(pageParam), + mode: 'imperative', + initialPageParam: 1, + }) + + observer.fetchNextPage({ pageParam: 2 }) + observer.fetchPreviousPage({ pageParam: 0, cancelRefetch: false }) + + // @ts-expect-error pageParam is required in imperative mode + observer.fetchNextPage() + + // @ts-expect-error pageParam is required in imperative mode + observer.fetchPreviousPage() + }) + + it('should reject missing mode / getNextPageParam', () => { + // @ts-expect-error getNextPageParam is required unless mode is imperative + new InfiniteQueryObserver(queryClient, { queryKey: queryKey(), queryFn: ({ pageParam }) => String(pageParam), initialPageParam: 1, }) + }) - expectTypeOf() - .parameter(0) - .toEqualTypeOf< - | { pageParam: number; cancelRefetch?: boolean; throwOnError?: boolean } - | undefined - >() - - expectTypeOf() - .parameter(0) - .toEqualTypeOf< - | { pageParam: number; cancelRefetch?: boolean; throwOnError?: boolean } - | undefined - >() + it('should reject page param getters in imperative mode', () => { + // @ts-expect-error getNextPageParam is not allowed in imperative mode + new InfiniteQueryObserver< + string, + Error, + InfiniteData, + ReturnType, + number, + 'imperative' + >(queryClient, { + queryKey: queryKey(), + queryFn: ({ pageParam }) => String(pageParam), + mode: 'imperative', + initialPageParam: 1, + getNextPageParam: () => 2, + }) + + // @ts-expect-error getPreviousPageParam is not allowed in imperative mode + new InfiniteQueryObserver< + string, + Error, + InfiniteData, + ReturnType, + number, + 'imperative' + >(queryClient, { + queryKey: queryKey(), + queryFn: ({ pageParam }) => String(pageParam), + mode: 'imperative', + initialPageParam: 1, + getPreviousPageParam: () => 0, + }) }) }) diff --git a/packages/query-core/src/__tests__/queryClient.test-d.tsx b/packages/query-core/src/__tests__/queryClient.test-d.tsx index 8a3be1a9e23..d8d66df86db 100644 --- a/packages/query-core/src/__tests__/queryClient.test-d.tsx +++ b/packages/query-core/src/__tests__/queryClient.test-d.tsx @@ -171,26 +171,51 @@ describe('fetchInfiniteQuery', () => { }) it('should not allow passing getNextPageParam without pages', () => { - assertType>([ - { - queryKey: ['key'], - queryFn: () => Promise.resolve('string'), - initialPageParam: 1, - getNextPageParam: () => 1, - }, - ]) + void new QueryClient().fetchInfiniteQuery({ + queryKey: ['key'], + queryFn: () => Promise.resolve('string'), + initialPageParam: 1, + getNextPageParam: () => 1, + }) }) it('should not allow passing pages without getNextPageParam', () => { - assertType>([ - // @ts-expect-error Property 'getNextPageParam' is missing - { - queryKey: ['key'], - queryFn: () => Promise.resolve('string'), - initialPageParam: 1, - pages: 5, - }, - ]) + // @ts-expect-error Property 'getNextPageParam' is missing + void new QueryClient().fetchInfiniteQuery({ + queryKey: ['key'], + queryFn: () => Promise.resolve('string'), + initialPageParam: 1, + pages: 5, + }) + }) + + it('should allow imperative mode without page param getters', () => { + void new QueryClient().fetchInfiniteQuery({ + queryKey: ['key'], + queryFn: () => Promise.resolve('string'), + mode: 'imperative', + initialPageParam: 1, + }) + }) + + it('should not allow imperative mode with page param getters or pages', () => { + // @ts-expect-error getNextPageParam is not allowed in imperative mode + void new QueryClient().fetchInfiniteQuery({ + queryKey: ['key'], + queryFn: () => Promise.resolve('string'), + mode: 'imperative', + initialPageParam: 1, + getNextPageParam: () => 1, + }) + + // @ts-expect-error pages are not allowed in imperative mode + void new QueryClient().fetchInfiniteQuery({ + queryKey: ['key'], + queryFn: () => Promise.resolve('string'), + mode: 'imperative', + initialPageParam: 1, + pages: 2, + }) }) }) diff --git a/packages/query-core/src/__tests__/queryClient.test.tsx b/packages/query-core/src/__tests__/queryClient.test.tsx index 2676717fb62..16ec4bace67 100644 --- a/packages/query-core/src/__tests__/queryClient.test.tsx +++ b/packages/query-core/src/__tests__/queryClient.test.tsx @@ -810,7 +810,12 @@ describe('queryClient', () => { StrictData, StrictQueryKey, number - >({ queryKey: key, queryFn: fetchFn, initialPageParam: 0 }), + >({ + queryKey: key, + queryFn: fetchFn, + initialPageParam: 0, + mode: 'imperative', + }), ).resolves.toEqual(data) }) @@ -819,6 +824,7 @@ describe('queryClient', () => { const result = await queryClient.fetchInfiniteQuery({ queryKey: key, initialPageParam: 10, + mode: 'imperative', queryFn: ({ pageParam }) => Number(pageParam), }) const result2 = queryClient.getQueryData(key) @@ -848,7 +854,12 @@ describe('queryClient', () => { StrictData, StrictQueryKey, number - >({ queryKey: key, queryFn: fetchFn, initialPageParam: 0 }) + >({ + queryKey: key, + queryFn: fetchFn, + initialPageParam: 0, + mode: 'imperative', + }) const result = queryClient.getQueryData(key) @@ -865,6 +876,7 @@ describe('queryClient', () => { queryKey: key, queryFn: ({ pageParam }) => Number(pageParam), initialPageParam: 10, + mode: 'imperative', }) const result = queryClient.getQueryData(key) diff --git a/packages/query-core/src/infiniteQueryBehavior.ts b/packages/query-core/src/infiniteQueryBehavior.ts index b161e81f0f6..8b1f55303b6 100644 --- a/packages/query-core/src/infiniteQueryBehavior.ts +++ b/packages/query-core/src/infiniteQueryBehavior.ts @@ -7,18 +7,29 @@ import { import type { QueryBehavior } from './query' import type { InfiniteData, + InfiniteQueryMode, InfiniteQueryPageParamsOptions, OmitKeyof, QueryFunctionContext, QueryKey, } from './types' -export function infiniteQueryBehavior( +export function infiniteQueryBehavior< + TQueryFnData, + TError, + TData, + TPageParam, + TMode extends 'declarative' | InfiniteQueryMode = 'declarative', +>( pages?: number, ): QueryBehavior> { return { onFetch: (context, query) => { - const options = context.options as InfiniteQueryPageParamsOptions + const options = context.options as InfiniteQueryPageParamsOptions< + TData, + TPageParam, + TMode + > const fetchMore = context.fetchOptions?.meta?.fetchMore const oldPages = context.state.data?.pages || [] const oldPageParams = context.state.data?.pageParams || [] @@ -88,13 +99,14 @@ export function infiniteQueryBehavior( pageParams: oldPageParams, } const param = - fetchMore.pageParam === undefined - ? pageParamFn(options, oldData) - : fetchMore.pageParam + options.mode === 'imperative' || fetchMore.pageParam === undefined + ? fetchMore.pageParam + : pageParamFn(options, oldData) result = await fetchPage(oldData, param, previous) } else { - const remainingPages = pages ?? oldPages.length + const remainingPages = + options.mode === 'imperative' ? 1 : (pages ?? oldPages.length) // Fetch all pages do { @@ -133,12 +145,16 @@ export function infiniteQueryBehavior( } function getNextPageParam( - options: InfiniteQueryPageParamsOptions, + options: InfiniteQueryPageParamsOptions, { pages, pageParams }: InfiniteData, ): unknown | undefined { + if (options.mode === 'imperative') { + return undefined + } + const lastIndex = pages.length - 1 return pages.length > 0 - ? options.getNextPageParam?.( + ? options.getNextPageParam( pages[lastIndex], pages, pageParams[lastIndex], @@ -148,9 +164,13 @@ function getNextPageParam( } function getPreviousPageParam( - options: InfiniteQueryPageParamsOptions, + options: InfiniteQueryPageParamsOptions, { pages, pageParams }: InfiniteData, ): unknown | undefined { + if (options.mode === 'imperative') { + return undefined + } + return pages.length > 0 ? options.getPreviousPageParam?.(pages[0], pages, pageParams[0], pageParams) : undefined @@ -160,10 +180,10 @@ function getPreviousPageParam( * Checks if there is a next page. */ export function hasNextPage( - options: InfiniteQueryPageParamsOptions, + options: InfiniteQueryPageParamsOptions, data?: InfiniteData, ): boolean { - if (!data) return false + if (!data || options.mode === 'imperative') return false return getNextPageParam(options, data) != null } @@ -171,9 +191,11 @@ export function hasNextPage( * Checks if there is a previous page. */ export function hasPreviousPage( - options: InfiniteQueryPageParamsOptions, + options: InfiniteQueryPageParamsOptions, data?: InfiniteData, ): boolean { - if (!data || !options.getPreviousPageParam) return false + if (!data || options.mode === 'imperative' || !options.getPreviousPageParam) { + return false + } return getPreviousPageParam(options, data) != null } diff --git a/packages/query-core/src/infiniteQueryObserver.ts b/packages/query-core/src/infiniteQueryObserver.ts index 8e29c3fc0f0..bb017e710e9 100644 --- a/packages/query-core/src/infiniteQueryObserver.ts +++ b/packages/query-core/src/infiniteQueryObserver.ts @@ -7,20 +7,30 @@ import { import type { Subscribable } from './subscribable' import type { DefaultError, - DefaultedInfiniteQueryObserverOptions, - FetchNextPageOptions, - FetchPreviousPageOptions, + DefaultedQueryObserverOptions, + FetchPageDirectionMode, InfiniteData, + InfiniteQueryFetchNextPageArgs, + InfiniteQueryFetchNextPageOptions, + InfiniteQueryFetchPreviousPageArgs, + InfiniteQueryFetchPreviousPageOptions, + InfiniteQueryMode, InfiniteQueryObserverBaseResult, InfiniteQueryObserverOptions, InfiniteQueryObserverResult, + QueryObserverOptions, QueryKey, } from './types' import type { QueryClient } from './queryClient' import type { Query } from './query' -type InfiniteQueryObserverListener = ( - result: InfiniteQueryObserverResult, +type InfiniteQueryObserverListener< + TData, + TError, + TPageParam, + TMode extends FetchPageDirectionMode, +> = ( + result: InfiniteQueryObserverResult, ) => void export class InfiniteQueryObserver< @@ -29,6 +39,7 @@ export class InfiniteQueryObserver< TData = InfiniteData, TQueryKey extends QueryKey = QueryKey, TPageParam = unknown, + TMode extends FetchPageDirectionMode = 'declarative', > extends QueryObserver< TQueryFnData, TError, @@ -38,9 +49,17 @@ export class InfiniteQueryObserver< > { // Type override subscribe!: Subscribable< - InfiniteQueryObserverListener + InfiniteQueryObserverListener >['subscribe'] + fetchNextPage!: ( + ...args: InfiniteQueryFetchNextPageArgs + ) => Promise> + + fetchPreviousPage!: ( + ...args: InfiniteQueryFetchPreviousPageArgs + ) => Promise> + // Type override getCurrentResult!: ReplaceReturnType< QueryObserver< @@ -50,7 +69,7 @@ export class InfiniteQueryObserver< InfiniteData, TQueryKey >['getCurrentResult'], - InfiniteQueryObserverResult + InfiniteQueryObserverResult > // Type override @@ -62,7 +81,7 @@ export class InfiniteQueryObserver< InfiniteData, TQueryKey >['fetch'], - Promise> + Promise> > constructor( @@ -72,7 +91,30 @@ export class InfiniteQueryObserver< TError, TData, TQueryKey, - TPageParam + TPageParam, + 'declarative' + >, + ) + constructor( + client: QueryClient, + options: InfiniteQueryObserverOptions< + TQueryFnData, + TError, + TData, + TQueryKey, + TPageParam, + InfiniteQueryMode + >, + ) + constructor( + client: QueryClient, + options: InfiniteQueryObserverOptions< + TQueryFnData, + TError, + TData, + TQueryKey, + TPageParam, + TMode >, ) { super(client, options) @@ -80,60 +122,82 @@ export class InfiniteQueryObserver< protected bindMethods(): void { super.bindMethods() - this.fetchNextPage = this.fetchNextPage.bind(this) - this.fetchPreviousPage = this.fetchPreviousPage.bind(this) + this.fetchNextPage = + this.#fetchNextPage.bind(this) as typeof this.fetchNextPage + this.fetchPreviousPage = + this.#fetchPreviousPage.bind(this) as typeof this.fetchPreviousPage } setOptions( - options: InfiniteQueryObserverOptions< + options: QueryObserverOptions< TQueryFnData, TError, TData, - TQueryKey, - TPageParam + InfiniteData, + TQueryKey >, ): void { super.setOptions({ - ...options, + ...(options as unknown as InfiniteQueryObserverOptions< + TQueryFnData, + TError, + TData, + TQueryKey, + TPageParam, + TMode + >), behavior: infiniteQueryBehavior(), - }) + } as any) } getOptimisticResult( - options: DefaultedInfiniteQueryObserverOptions< + options: DefaultedQueryObserverOptions< TQueryFnData, TError, TData, - TQueryKey, - TPageParam + InfiniteData, + TQueryKey >, - ): InfiniteQueryObserverResult { + ): InfiniteQueryObserverResult { options.behavior = infiniteQueryBehavior() return super.getOptimisticResult(options) as InfiniteQueryObserverResult< TData, - TError + TError, + TPageParam, + TMode > } - fetchNextPage({ pageParam, ...options }: FetchNextPageOptions = {}): Promise< - InfiniteQueryObserverResult - > { + #fetchNextPage( + options?: InfiniteQueryFetchNextPageOptions, + ): Promise> { + const pageParam = (options as { pageParam?: TPageParam } | undefined) + ?.pageParam + const fetchOptions = { ...(options ?? {}) } as Record + delete fetchOptions.pageParam return this.fetch({ - ...options, + ...(fetchOptions as unknown as InfiniteQueryFetchNextPageOptions< + TPageParam, + TMode + >), meta: { fetchMore: { direction: 'forward', pageParam }, }, }) } - fetchPreviousPage({ - pageParam, - ...options - }: FetchPreviousPageOptions = {}): Promise< - InfiniteQueryObserverResult - > { + #fetchPreviousPage( + options?: InfiniteQueryFetchPreviousPageOptions, + ): Promise> { + const pageParam = (options as { pageParam?: TPageParam } | undefined) + ?.pageParam + const fetchOptions = { ...(options ?? {}) } as Record + delete fetchOptions.pageParam return this.fetch({ - ...options, + ...(fetchOptions as unknown as InfiniteQueryFetchPreviousPageOptions< + TPageParam, + TMode + >), meta: { fetchMore: { direction: 'backward', pageParam }, }, @@ -141,6 +205,34 @@ export class InfiniteQueryObserver< } protected createResult( + query: Query< + TQueryFnData, + TError, + InfiniteData, + TQueryKey + >, + options: QueryObserverOptions< + TQueryFnData, + TError, + TData, + InfiniteData, + TQueryKey + >, + ): InfiniteQueryObserverResult { + return this.#createResult( + query, + options as unknown as InfiniteQueryObserverOptions< + TQueryFnData, + TError, + TData, + TQueryKey, + TPageParam, + TMode + >, + ) + } + + #createResult( query: Query< TQueryFnData, TError, @@ -152,11 +244,21 @@ export class InfiniteQueryObserver< TError, TData, TQueryKey, - TPageParam + TPageParam, + TMode >, - ): InfiniteQueryObserverResult { + ): InfiniteQueryObserverResult { const { state } = query - const parentResult = super.createResult(query, options) + const parentResult = super.createResult( + query, + options as QueryObserverOptions< + TQueryFnData, + TError, + TData, + InfiniteData, + TQueryKey + >, + ) const { isFetching, isRefetching, isError, isRefetchError } = parentResult const fetchDirection = state.fetchMeta?.fetchMore?.direction @@ -167,7 +269,12 @@ export class InfiniteQueryObserver< const isFetchPreviousPageError = isError && fetchDirection === 'backward' const isFetchingPreviousPage = isFetching && fetchDirection === 'backward' - const result: InfiniteQueryObserverBaseResult = { + const result: InfiniteQueryObserverBaseResult< + TData, + TError, + TPageParam, + TMode + > = { ...parentResult, fetchNextPage: this.fetchNextPage, fetchPreviousPage: this.fetchPreviousPage, @@ -183,7 +290,7 @@ export class InfiniteQueryObserver< isRefetching && !isFetchingNextPage && !isFetchingPreviousPage, } - return result as InfiniteQueryObserverResult + return result as InfiniteQueryObserverResult } } diff --git a/packages/query-core/src/queryClient.ts b/packages/query-core/src/queryClient.ts index 80cc36668aa..d64dacfc409 100644 --- a/packages/query-core/src/queryClient.ts +++ b/packages/query-core/src/queryClient.ts @@ -20,6 +20,7 @@ import type { DefaultedQueryObserverOptions, EnsureInfiniteQueryDataOptions, EnsureQueryDataOptions, + FetchPageDirectionMode, FetchInfiniteQueryOptions, FetchQueryOptions, InferDataFromTag, @@ -392,14 +393,49 @@ export class QueryClient { TError, TData, TQueryKey, - TPageParam + TPageParam, + 'declarative' + >, + ): Promise> + fetchInfiniteQuery< + TQueryFnData, + TError = DefaultError, + TData = TQueryFnData, + TQueryKey extends QueryKey = QueryKey, + TPageParam = unknown, + >( + options: FetchInfiniteQueryOptions< + TQueryFnData, + TError, + TData, + TQueryKey, + TPageParam, + 'imperative' + >, + ): Promise> + fetchInfiniteQuery< + TQueryFnData, + TError = DefaultError, + TData = TQueryFnData, + TQueryKey extends QueryKey = QueryKey, + TPageParam = unknown, + TMode extends FetchPageDirectionMode = 'declarative', + >( + options: FetchInfiniteQueryOptions< + TQueryFnData, + TError, + TData, + TQueryKey, + TPageParam, + TMode >, ): Promise> { options.behavior = infiniteQueryBehavior< TQueryFnData, TError, TData, - TPageParam + TPageParam, + TMode >(options.pages) return this.fetchQuery(options as any) } @@ -416,10 +452,44 @@ export class QueryClient { TError, TData, TQueryKey, - TPageParam + TPageParam, + 'declarative' + >, + ): Promise + prefetchInfiniteQuery< + TQueryFnData, + TError = DefaultError, + TData = TQueryFnData, + TQueryKey extends QueryKey = QueryKey, + TPageParam = unknown, + >( + options: FetchInfiniteQueryOptions< + TQueryFnData, + TError, + TData, + TQueryKey, + TPageParam, + 'imperative' + >, + ): Promise + prefetchInfiniteQuery< + TQueryFnData, + TError = DefaultError, + TData = TQueryFnData, + TQueryKey extends QueryKey = QueryKey, + TPageParam = unknown, + TMode extends FetchPageDirectionMode = 'declarative', + >( + options: FetchInfiniteQueryOptions< + TQueryFnData, + TError, + TData, + TQueryKey, + TPageParam, + TMode >, ): Promise { - return this.fetchInfiniteQuery(options).then(noop).catch(noop) + return this.fetchInfiniteQuery(options as any).then(noop).catch(noop) } ensureInfiniteQueryData< @@ -434,14 +504,49 @@ export class QueryClient { TError, TData, TQueryKey, - TPageParam + TPageParam, + 'declarative' + >, + ): Promise> + ensureInfiniteQueryData< + TQueryFnData, + TError = DefaultError, + TData = TQueryFnData, + TQueryKey extends QueryKey = QueryKey, + TPageParam = unknown, + >( + options: EnsureInfiniteQueryDataOptions< + TQueryFnData, + TError, + TData, + TQueryKey, + TPageParam, + 'imperative' + >, + ): Promise> + ensureInfiniteQueryData< + TQueryFnData, + TError = DefaultError, + TData = TQueryFnData, + TQueryKey extends QueryKey = QueryKey, + TPageParam = unknown, + TMode extends FetchPageDirectionMode = 'declarative', + >( + options: EnsureInfiniteQueryDataOptions< + TQueryFnData, + TError, + TData, + TQueryKey, + TPageParam, + TMode >, ): Promise> { options.behavior = infiniteQueryBehavior< TQueryFnData, TError, TData, - TPageParam + TPageParam, + TMode >(options.pages) return this.ensureQueryData(options as any) diff --git a/packages/query-core/src/types.ts b/packages/query-core/src/types.ts index 270b66555e9..6dc87085060 100644 --- a/packages/query-core/src/types.ts +++ b/packages/query-core/src/types.ts @@ -281,10 +281,13 @@ export interface InitialPageParam { initialPageParam: TPageParam } -export interface InfiniteQueryPageParamsOptions< +export type InfiniteQueryMode = 'imperative' + +export interface InfiniteQueryPageParamsDeclarativeOptions< TQueryFnData = unknown, TPageParam = unknown, > extends InitialPageParam { + mode?: never /** * This function can be set to automatically get the previous cursor for infinite queries. * The result will also be used to determine the value of `hasPreviousPage`. @@ -294,7 +297,32 @@ export interface InfiniteQueryPageParamsOptions< * 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`. */ - getNextPageParam?: GetNextPageParamFunction + getNextPageParam: GetNextPageParamFunction +} + +export interface InfiniteQueryPageParamsImperativeOptions< + TPageParam = unknown, +> extends InitialPageParam { + mode: InfiniteQueryMode + getPreviousPageParam?: never + getNextPageParam?: never +} + +export type InfiniteQueryPageParamsOptions< + TQueryFnData = unknown, + TPageParam = unknown, + TMode extends 'declarative' | InfiniteQueryMode = 'declarative', +> = TMode extends InfiniteQueryMode + ? InfiniteQueryPageParamsImperativeOptions + : InfiniteQueryPageParamsDeclarativeOptions + +export type FetchPageDirectionMode = 'declarative' | InfiniteQueryMode + +export interface ImperativeFetchPageOptions { + /** + * The page param to pass to the query function for this imperative fetch. + */ + pageParam: TPageParam } export type ThrowOnError< @@ -455,23 +483,22 @@ export type DefaultedQueryObserverOptions< 'throwOnError' | 'refetchOnReconnect' | 'queryHash' > -export interface InfiniteQueryObserverOptions< +export type InfiniteQueryObserverOptions< TQueryFnData = unknown, TError = DefaultError, TData = TQueryFnData, TQueryKey extends QueryKey = QueryKey, TPageParam = unknown, -> - extends - QueryObserverOptions< - TQueryFnData, - TError, - TData, - InfiniteData, - TQueryKey, - TPageParam - >, - InfiniteQueryPageParamsOptions {} + TMode extends FetchPageDirectionMode = 'declarative', +> = QueryObserverOptions< + TQueryFnData, + TError, + TData, + InfiniteData, + TQueryKey, + TPageParam +> & + InfiniteQueryPageParamsOptions export type DefaultedInfiniteQueryObserverOptions< TQueryFnData = unknown, @@ -479,13 +506,15 @@ export type DefaultedInfiniteQueryObserverOptions< TData = TQueryFnData, TQueryKey extends QueryKey = QueryKey, TPageParam = unknown, + TMode extends FetchPageDirectionMode = 'declarative', > = WithRequired< InfiniteQueryObserverOptions< TQueryFnData, TError, TData, TQueryKey, - TPageParam + TPageParam, + TMode >, 'throwOnError' | 'refetchOnReconnect' | 'queryHash' > @@ -530,29 +559,48 @@ export type EnsureInfiniteQueryDataOptions< TData = TQueryFnData, TQueryKey extends QueryKey = QueryKey, TPageParam = unknown, + TMode extends FetchPageDirectionMode = 'declarative', > = FetchInfiniteQueryOptions< TQueryFnData, TError, TData, TQueryKey, - TPageParam + TPageParam, + TMode > & { revalidateIfStale?: boolean } -type FetchInfiniteQueryPages = +type FetchInfiniteQueryPagesDeclarative< + TQueryFnData = unknown, + TPageParam = unknown, +> = | { pages?: never } | { pages: number getNextPageParam: GetNextPageParamFunction } +type FetchInfiniteQueryPages< + TQueryFnData = unknown, + TPageParam = unknown, + TMode extends FetchPageDirectionMode = 'declarative', +> = TMode extends InfiniteQueryMode + ? { + mode: InfiniteQueryMode + pages?: never + getNextPageParam?: never + getPreviousPageParam?: never + } + : FetchInfiniteQueryPagesDeclarative + export type FetchInfiniteQueryOptions< TQueryFnData = unknown, TError = DefaultError, TData = TQueryFnData, TQueryKey extends QueryKey = QueryKey, TPageParam = unknown, + TMode extends FetchPageDirectionMode = 'declarative', > = Omit< FetchQueryOptions< TQueryFnData, @@ -563,8 +611,8 @@ export type FetchInfiniteQueryOptions< >, 'initialPageParam' > & - InitialPageParam & - FetchInfiniteQueryPages + InfiniteQueryPageParamsOptions & + FetchInfiniteQueryPages export interface ResultOptions { throwOnError?: boolean @@ -618,6 +666,34 @@ export interface FetchPreviousPageOptions extends ResultOptions { cancelRefetch?: boolean } +export type InfiniteQueryFetchNextPageOptions< + TPageParam = unknown, + TMode extends FetchPageDirectionMode = 'declarative', +> = TMode extends InfiniteQueryMode + ? ImperativeFetchPageOptions & FetchNextPageOptions + : FetchNextPageOptions + +export type InfiniteQueryFetchPreviousPageOptions< + TPageParam = unknown, + TMode extends FetchPageDirectionMode = 'declarative', +> = TMode extends InfiniteQueryMode + ? ImperativeFetchPageOptions & FetchPreviousPageOptions + : FetchPreviousPageOptions + +export type InfiniteQueryFetchNextPageArgs< + TPageParam = unknown, + TMode extends FetchPageDirectionMode = 'declarative', +> = TMode extends InfiniteQueryMode + ? [options: InfiniteQueryFetchNextPageOptions] + : [options?: InfiniteQueryFetchNextPageOptions] + +export type InfiniteQueryFetchPreviousPageArgs< + TPageParam = unknown, + TMode extends FetchPageDirectionMode = 'declarative', +> = TMode extends InfiniteQueryMode + ? [options: InfiniteQueryFetchPreviousPageOptions] + : [options?: InfiniteQueryFetchPreviousPageOptions] + export type QueryStatus = 'pending' | 'error' | 'success' export type FetchStatus = 'fetching' | 'paused' | 'idle' @@ -910,19 +986,21 @@ export type QueryObserverResult = export interface InfiniteQueryObserverBaseResult< TData = unknown, TError = DefaultError, + TPageParam = unknown, + TMode extends FetchPageDirectionMode = 'declarative', > extends QueryObserverBaseResult { /** * This function allows you to fetch the next "page" of results. */ fetchNextPage: ( - options?: FetchNextPageOptions, - ) => Promise> + ...args: InfiniteQueryFetchNextPageArgs + ) => Promise> /** * This function allows you to fetch the previous "page" of results. */ fetchPreviousPage: ( - options?: FetchPreviousPageOptions, - ) => Promise> + ...args: InfiniteQueryFetchPreviousPageArgs + ) => Promise> /** * Will be `true` if there is a next page to be fetched (known via the `getNextPageParam` option). */ @@ -952,7 +1030,9 @@ export interface InfiniteQueryObserverBaseResult< export interface InfiniteQueryObserverPendingResult< TData = unknown, TError = DefaultError, -> extends InfiniteQueryObserverBaseResult { + TPageParam = unknown, + TMode extends FetchPageDirectionMode = 'declarative', +> extends InfiniteQueryObserverBaseResult { data: undefined error: null isError: false @@ -969,7 +1049,9 @@ export interface InfiniteQueryObserverPendingResult< export interface InfiniteQueryObserverLoadingResult< TData = unknown, TError = DefaultError, -> extends InfiniteQueryObserverBaseResult { + TPageParam = unknown, + TMode extends FetchPageDirectionMode = 'declarative', +> extends InfiniteQueryObserverBaseResult { data: undefined error: null isError: false @@ -987,7 +1069,9 @@ export interface InfiniteQueryObserverLoadingResult< export interface InfiniteQueryObserverLoadingErrorResult< TData = unknown, TError = DefaultError, -> extends InfiniteQueryObserverBaseResult { + TPageParam = unknown, + TMode extends FetchPageDirectionMode = 'declarative', +> extends InfiniteQueryObserverBaseResult { data: undefined error: TError isError: true @@ -1005,7 +1089,9 @@ export interface InfiniteQueryObserverLoadingErrorResult< export interface InfiniteQueryObserverRefetchErrorResult< TData = unknown, TError = DefaultError, -> extends InfiniteQueryObserverBaseResult { + TPageParam = unknown, + TMode extends FetchPageDirectionMode = 'declarative', +> extends InfiniteQueryObserverBaseResult { data: TData error: TError isError: true @@ -1021,7 +1107,9 @@ export interface InfiniteQueryObserverRefetchErrorResult< export interface InfiniteQueryObserverSuccessResult< TData = unknown, TError = DefaultError, -> extends InfiniteQueryObserverBaseResult { + TPageParam = unknown, + TMode extends FetchPageDirectionMode = 'declarative', +> extends InfiniteQueryObserverBaseResult { data: TData error: null isError: false @@ -1039,7 +1127,9 @@ export interface InfiniteQueryObserverSuccessResult< export interface InfiniteQueryObserverPlaceholderResult< TData = unknown, TError = DefaultError, -> extends InfiniteQueryObserverBaseResult { + TPageParam = unknown, + TMode extends FetchPageDirectionMode = 'declarative', +> extends InfiniteQueryObserverBaseResult { data: TData isError: false error: null @@ -1057,19 +1147,23 @@ export interface InfiniteQueryObserverPlaceholderResult< export type DefinedInfiniteQueryObserverResult< TData = unknown, TError = DefaultError, + TPageParam = unknown, + TMode extends FetchPageDirectionMode = 'declarative', > = - | InfiniteQueryObserverRefetchErrorResult - | InfiniteQueryObserverSuccessResult + | InfiniteQueryObserverRefetchErrorResult + | InfiniteQueryObserverSuccessResult export type InfiniteQueryObserverResult< TData = unknown, TError = DefaultError, + TPageParam = unknown, + TMode extends FetchPageDirectionMode = 'declarative', > = - | DefinedInfiniteQueryObserverResult - | InfiniteQueryObserverLoadingErrorResult - | InfiniteQueryObserverLoadingResult - | InfiniteQueryObserverPendingResult - | InfiniteQueryObserverPlaceholderResult + | DefinedInfiniteQueryObserverResult + | InfiniteQueryObserverLoadingErrorResult + | InfiniteQueryObserverLoadingResult + | InfiniteQueryObserverPendingResult + | InfiniteQueryObserverPlaceholderResult export type MutationKey = Register extends { mutationKey: infer TMutationKey diff --git a/packages/react-query/src/__tests__/infiniteQueryOptions.test-d.tsx b/packages/react-query/src/__tests__/infiniteQueryOptions.test-d.tsx index a1d97bf0927..ec54f5f1e7c 100644 --- a/packages/react-query/src/__tests__/infiniteQueryOptions.test-d.tsx +++ b/packages/react-query/src/__tests__/infiniteQueryOptions.test-d.tsx @@ -39,16 +39,21 @@ describe('infiniteQueryOptions', () => { const options = infiniteQueryOptions({ queryKey: ['key'], queryFn: () => Promise.resolve('string'), - getNextPageParam: () => 1, initialPageParam: 1, + mode: 'imperative', }) - const { data } = useInfiniteQuery(options) + const { data, fetchNextPage, fetchPreviousPage } = useInfiniteQuery(options) // known issue: type of pageParams is unknown when returned from useInfiniteQuery expectTypeOf(data).toEqualTypeOf< InfiniteData | undefined >() + fetchNextPage({ pageParam: 2 }) + fetchPreviousPage({ pageParam: 0 }) + + // @ts-expect-error pageParam is required in imperative mode + fetchNextPage() }) it('should work when passed to useSuspenseInfiniteQuery', () => { const options = infiniteQueryOptions({ @@ -66,8 +71,8 @@ describe('infiniteQueryOptions', () => { const options = infiniteQueryOptions({ queryKey: ['key'], queryFn: () => Promise.resolve('string'), - getNextPageParam: () => 1, initialPageParam: 1, + mode: 'imperative', }) const data = await new QueryClient().fetchInfiniteQuery(options) @@ -153,6 +158,33 @@ describe('infiniteQueryOptions', () => { expectTypeOf(data).toEqualTypeOf>() }) + it('should reject missing mode / getNextPageParam and reject getters in imperative mode', () => { + // @ts-expect-error getNextPageParam is required unless mode is imperative + infiniteQueryOptions({ + queryKey: ['key'], + queryFn: () => Promise.resolve('string'), + initialPageParam: 1, + }) + + infiniteQueryOptions({ + queryKey: ['key'], + queryFn: () => Promise.resolve('string'), + initialPageParam: 1, + mode: 'imperative', + // @ts-expect-error getNextPageParam is not allowed in imperative mode + getNextPageParam: () => 1, + }) + + infiniteQueryOptions({ + queryKey: ['key'], + queryFn: () => Promise.resolve('string'), + initialPageParam: 1, + mode: 'imperative', + // @ts-expect-error getPreviousPageParam is not allowed in imperative mode + getPreviousPageParam: () => 0, + }) + }) + test('should not be allowed to be passed to non-infinite query functions', () => { const queryClient = new QueryClient() const options = infiniteQueryOptions({ diff --git a/packages/react-query/src/__tests__/ssr.test.tsx b/packages/react-query/src/__tests__/ssr.test.tsx index 3cd16c7ee92..bdb3f090257 100644 --- a/packages/react-query/src/__tests__/ssr.test.tsx +++ b/packages/react-query/src/__tests__/ssr.test.tsx @@ -156,6 +156,7 @@ describe('Server Side Rendering', () => { queryKey: key, queryFn, initialPageParam: 0, + mode: 'imperative', }) await vi.advanceTimersByTimeAsync(10) diff --git a/packages/react-query/src/__tests__/useInfiniteQuery.test-d.tsx b/packages/react-query/src/__tests__/useInfiniteQuery.test-d.tsx index a231d206008..ea55f2515a9 100644 --- a/packages/react-query/src/__tests__/useInfiniteQuery.test-d.tsx +++ b/packages/react-query/src/__tests__/useInfiniteQuery.test-d.tsx @@ -34,6 +34,7 @@ describe('pageParam', () => { expectTypeOf(pageParam).toEqualTypeOf() }, initialPageParam: 1, + mode: 'imperative', }) }) @@ -45,8 +46,27 @@ describe('pageParam', () => { expectTypeOf(pageParam).toEqualTypeOf() }, initialPageParam: 1, + mode: 'imperative', }) }) + + it('should require pageParam on imperative fetch methods', () => { + const infiniteQuery = useInfiniteQuery({ + queryKey: ['key'], + queryFn: ({ pageParam }) => { + expectTypeOf(pageParam).toEqualTypeOf() + return pageParam * 5 + }, + initialPageParam: 1, + mode: 'imperative', + }) + + infiniteQuery.fetchNextPage({ pageParam: 2 }) + infiniteQuery.fetchPreviousPage({ pageParam: 0 }) + + // @ts-expect-error pageParam is required in imperative mode + infiniteQuery.fetchNextPage() + }) }) describe('select', () => { it('should still return paginated data if no select result', () => { diff --git a/packages/react-query/src/infiniteQueryOptions.ts b/packages/react-query/src/infiniteQueryOptions.ts index 5e8c371a59f..640ddbf6fcf 100644 --- a/packages/react-query/src/infiniteQueryOptions.ts +++ b/packages/react-query/src/infiniteQueryOptions.ts @@ -1,6 +1,7 @@ import type { DataTag, DefaultError, + FetchPageDirectionMode, InfiniteData, InitialDataFunction, NonUndefinedGuard, @@ -16,12 +17,14 @@ export type UndefinedInitialDataInfiniteOptions< TData = InfiniteData, TQueryKey extends QueryKey = QueryKey, TPageParam = unknown, + TMode extends FetchPageDirectionMode = 'declarative', > = UseInfiniteQueryOptions< TQueryFnData, TError, TData, TQueryKey, - TPageParam + TPageParam, + TMode > & { initialData?: | undefined @@ -37,8 +40,16 @@ export type UnusedSkipTokenInfiniteOptions< TData = InfiniteData, TQueryKey extends QueryKey = QueryKey, TPageParam = unknown, + TMode extends FetchPageDirectionMode = 'declarative', > = OmitKeyof< - UseInfiniteQueryOptions, + UseInfiniteQueryOptions< + TQueryFnData, + TError, + TData, + TQueryKey, + TPageParam, + TMode + >, 'queryFn' > & { queryFn?: Exclude< @@ -47,7 +58,8 @@ export type UnusedSkipTokenInfiniteOptions< TError, TData, TQueryKey, - TPageParam + TPageParam, + TMode >['queryFn'], SkipToken | undefined > @@ -59,12 +71,14 @@ export type DefinedInitialDataInfiniteOptions< TData = InfiniteData, TQueryKey extends QueryKey = QueryKey, TPageParam = unknown, + TMode extends FetchPageDirectionMode = 'declarative', > = UseInfiniteQueryOptions< TQueryFnData, TError, TData, TQueryKey, - TPageParam + TPageParam, + TMode > & { initialData: | NonUndefinedGuard> @@ -78,20 +92,23 @@ export function infiniteQueryOptions< TData = InfiniteData, TQueryKey extends QueryKey = QueryKey, TPageParam = unknown, + TMode extends FetchPageDirectionMode = 'declarative', >( options: DefinedInitialDataInfiniteOptions< TQueryFnData, TError, TData, TQueryKey, - TPageParam + TPageParam, + TMode >, ): DefinedInitialDataInfiniteOptions< TQueryFnData, TError, TData, TQueryKey, - TPageParam + TPageParam, + TMode > & { queryKey: DataTag, TError> } @@ -102,20 +119,23 @@ export function infiniteQueryOptions< TData = InfiniteData, TQueryKey extends QueryKey = QueryKey, TPageParam = unknown, + TMode extends FetchPageDirectionMode = 'declarative', >( options: UnusedSkipTokenInfiniteOptions< TQueryFnData, TError, TData, TQueryKey, - TPageParam + TPageParam, + TMode >, ): UnusedSkipTokenInfiniteOptions< TQueryFnData, TError, TData, TQueryKey, - TPageParam + TPageParam, + TMode > & { queryKey: DataTag, TError> } @@ -126,20 +146,23 @@ export function infiniteQueryOptions< TData = InfiniteData, TQueryKey extends QueryKey = QueryKey, TPageParam = unknown, + TMode extends FetchPageDirectionMode = 'declarative', >( options: UndefinedInitialDataInfiniteOptions< TQueryFnData, TError, TData, TQueryKey, - TPageParam + TPageParam, + TMode >, ): UndefinedInitialDataInfiniteOptions< TQueryFnData, TError, TData, TQueryKey, - TPageParam + TPageParam, + TMode > & { queryKey: DataTag, TError> } diff --git a/packages/react-query/src/types.ts b/packages/react-query/src/types.ts index 50df2d333f2..c7f0fbce368 100644 --- a/packages/react-query/src/types.ts +++ b/packages/react-query/src/types.ts @@ -5,6 +5,7 @@ import type { DefinedInfiniteQueryObserverResult, DefinedQueryObserverResult, DistributiveOmit, + FetchPageDirectionMode, FetchQueryOptions, InfiniteQueryObserverOptions, InfiniteQueryObserverResult, @@ -100,22 +101,24 @@ export type AnyUseInfiniteQueryOptions = UseInfiniteQueryOptions< any, any > -export interface UseInfiniteQueryOptions< +export type UseInfiniteQueryOptions< TQueryFnData = unknown, TError = DefaultError, TData = TQueryFnData, TQueryKey extends QueryKey = QueryKey, TPageParam = unknown, -> extends OmitKeyof< + TMode extends FetchPageDirectionMode = 'declarative', +> = DistributiveOmit< InfiniteQueryObserverOptions< TQueryFnData, TError, TData, TQueryKey, - TPageParam + TPageParam, + TMode >, 'suspense' -> { +> & { /** * Set this to `false` to unsubscribe this observer from updates to the query cache. * Defaults to `true`. @@ -125,16 +128,24 @@ export interface UseInfiniteQueryOptions< export type AnyUseSuspenseInfiniteQueryOptions = UseSuspenseInfiniteQueryOptions -export interface UseSuspenseInfiniteQueryOptions< +export type UseSuspenseInfiniteQueryOptions< TQueryFnData = unknown, TError = DefaultError, TData = TQueryFnData, TQueryKey extends QueryKey = QueryKey, TPageParam = unknown, -> extends OmitKeyof< - UseInfiniteQueryOptions, + TMode extends FetchPageDirectionMode = 'declarative', +> = DistributiveOmit< + UseInfiniteQueryOptions< + TQueryFnData, + TError, + TData, + TQueryKey, + TPageParam, + TMode + >, 'queryFn' | 'enabled' | 'throwOnError' | 'placeholderData' -> { +> & { queryFn?: Exclude< UseInfiniteQueryOptions< TQueryFnData, diff --git a/packages/react-query/src/useInfiniteQuery.ts b/packages/react-query/src/useInfiniteQuery.ts index 32ebfb7673e..113ce369dd5 100644 --- a/packages/react-query/src/useInfiniteQuery.ts +++ b/packages/react-query/src/useInfiniteQuery.ts @@ -3,6 +3,7 @@ import { InfiniteQueryObserver } from '@tanstack/query-core' import { useBaseQuery } from './useBaseQuery' import type { DefaultError, + FetchPageDirectionMode, InfiniteData, QueryClient, QueryKey, @@ -24,13 +25,15 @@ export function useInfiniteQuery< TData = InfiniteData, TQueryKey extends QueryKey = QueryKey, TPageParam = unknown, + TMode extends FetchPageDirectionMode = 'declarative', >( options: DefinedInitialDataInfiniteOptions< TQueryFnData, TError, TData, TQueryKey, - TPageParam + TPageParam, + TMode >, queryClient?: QueryClient, ): DefinedUseInfiniteQueryResult @@ -41,13 +44,15 @@ export function useInfiniteQuery< TData = InfiniteData, TQueryKey extends QueryKey = QueryKey, TPageParam = unknown, + TMode extends FetchPageDirectionMode = 'declarative', >( options: UndefinedInitialDataInfiniteOptions< TQueryFnData, TError, TData, TQueryKey, - TPageParam + TPageParam, + TMode >, queryClient?: QueryClient, ): UseInfiniteQueryResult @@ -58,13 +63,15 @@ export function useInfiniteQuery< TData = InfiniteData, TQueryKey extends QueryKey = QueryKey, TPageParam = unknown, + TMode extends FetchPageDirectionMode = 'declarative', >( options: UseInfiniteQueryOptions< TQueryFnData, TError, TData, TQueryKey, - TPageParam + TPageParam, + TMode >, queryClient?: QueryClient, ): UseInfiniteQueryResult From 9217eb35f0333578a2fd51517b59d5e5db0e70ff Mon Sep 17 00:00:00 2001 From: TkDodo Date: Fri, 3 Apr 2026 15:44:04 +0200 Subject: [PATCH 04/25] turn off vitest/expect-expect for type test files --- eslint.config.js | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/eslint.config.js b/eslint.config.js index d9a08b85eee..85033c6a871 100644 --- a/eslint.config.js +++ b/eslint.config.js @@ -61,4 +61,10 @@ export default [ }, settings: { vitest: { typecheck: true } }, }, + { + files: ['**/*.test-d.ts', '**/*.test-d.tsx'], + rules: { + 'vitest/expect-expect': 'off', + }, + }, ] From 3ab273a20b2a418e3b74286e66002373037ffaeb Mon Sep 17 00:00:00 2001 From: TkDodo Date: Fri, 3 Apr 2026 15:47:10 +0200 Subject: [PATCH 05/25] get rid of 'declarative' type --- .../query-core/src/infiniteQueryBehavior.ts | 2 +- .../query-core/src/infiniteQueryObserver.ts | 4 +- packages/query-core/src/queryClient.ts | 12 +++--- packages/query-core/src/types.ts | 40 +++++++++---------- .../react-query/src/infiniteQueryOptions.ts | 12 +++--- packages/react-query/src/types.ts | 4 +- packages/react-query/src/useInfiniteQuery.ts | 6 +-- 7 files changed, 40 insertions(+), 40 deletions(-) diff --git a/packages/query-core/src/infiniteQueryBehavior.ts b/packages/query-core/src/infiniteQueryBehavior.ts index 8b1f55303b6..0bc088b06fe 100644 --- a/packages/query-core/src/infiniteQueryBehavior.ts +++ b/packages/query-core/src/infiniteQueryBehavior.ts @@ -19,7 +19,7 @@ export function infiniteQueryBehavior< TError, TData, TPageParam, - TMode extends 'declarative' | InfiniteQueryMode = 'declarative', + TMode extends InfiniteQueryMode | undefined = undefined, >( pages?: number, ): QueryBehavior> { diff --git a/packages/query-core/src/infiniteQueryObserver.ts b/packages/query-core/src/infiniteQueryObserver.ts index bb017e710e9..c01d1160afa 100644 --- a/packages/query-core/src/infiniteQueryObserver.ts +++ b/packages/query-core/src/infiniteQueryObserver.ts @@ -39,7 +39,7 @@ export class InfiniteQueryObserver< TData = InfiniteData, TQueryKey extends QueryKey = QueryKey, TPageParam = unknown, - TMode extends FetchPageDirectionMode = 'declarative', + TMode extends FetchPageDirectionMode = undefined, > extends QueryObserver< TQueryFnData, TError, @@ -92,7 +92,7 @@ export class InfiniteQueryObserver< TData, TQueryKey, TPageParam, - 'declarative' + undefined >, ) constructor( diff --git a/packages/query-core/src/queryClient.ts b/packages/query-core/src/queryClient.ts index d64dacfc409..fb13964a307 100644 --- a/packages/query-core/src/queryClient.ts +++ b/packages/query-core/src/queryClient.ts @@ -394,7 +394,7 @@ export class QueryClient { TData, TQueryKey, TPageParam, - 'declarative' + undefined >, ): Promise> fetchInfiniteQuery< @@ -419,7 +419,7 @@ export class QueryClient { TData = TQueryFnData, TQueryKey extends QueryKey = QueryKey, TPageParam = unknown, - TMode extends FetchPageDirectionMode = 'declarative', + TMode extends FetchPageDirectionMode = undefined, >( options: FetchInfiniteQueryOptions< TQueryFnData, @@ -453,7 +453,7 @@ export class QueryClient { TData, TQueryKey, TPageParam, - 'declarative' + undefined >, ): Promise prefetchInfiniteQuery< @@ -478,7 +478,7 @@ export class QueryClient { TData = TQueryFnData, TQueryKey extends QueryKey = QueryKey, TPageParam = unknown, - TMode extends FetchPageDirectionMode = 'declarative', + TMode extends FetchPageDirectionMode = undefined, >( options: FetchInfiniteQueryOptions< TQueryFnData, @@ -505,7 +505,7 @@ export class QueryClient { TData, TQueryKey, TPageParam, - 'declarative' + undefined >, ): Promise> ensureInfiniteQueryData< @@ -530,7 +530,7 @@ export class QueryClient { TData = TQueryFnData, TQueryKey extends QueryKey = QueryKey, TPageParam = unknown, - TMode extends FetchPageDirectionMode = 'declarative', + TMode extends FetchPageDirectionMode = undefined, >( options: EnsureInfiniteQueryDataOptions< TQueryFnData, diff --git a/packages/query-core/src/types.ts b/packages/query-core/src/types.ts index 6dc87085060..37fd193da68 100644 --- a/packages/query-core/src/types.ts +++ b/packages/query-core/src/types.ts @@ -311,12 +311,12 @@ export interface InfiniteQueryPageParamsImperativeOptions< export type InfiniteQueryPageParamsOptions< TQueryFnData = unknown, TPageParam = unknown, - TMode extends 'declarative' | InfiniteQueryMode = 'declarative', + TMode extends InfiniteQueryMode | undefined = undefined, > = TMode extends InfiniteQueryMode ? InfiniteQueryPageParamsImperativeOptions : InfiniteQueryPageParamsDeclarativeOptions -export type FetchPageDirectionMode = 'declarative' | InfiniteQueryMode +export type FetchPageDirectionMode = InfiniteQueryMode | undefined export interface ImperativeFetchPageOptions { /** @@ -489,7 +489,7 @@ export type InfiniteQueryObserverOptions< TData = TQueryFnData, TQueryKey extends QueryKey = QueryKey, TPageParam = unknown, - TMode extends FetchPageDirectionMode = 'declarative', + TMode extends FetchPageDirectionMode = undefined, > = QueryObserverOptions< TQueryFnData, TError, @@ -506,7 +506,7 @@ export type DefaultedInfiniteQueryObserverOptions< TData = TQueryFnData, TQueryKey extends QueryKey = QueryKey, TPageParam = unknown, - TMode extends FetchPageDirectionMode = 'declarative', + TMode extends FetchPageDirectionMode = undefined, > = WithRequired< InfiniteQueryObserverOptions< TQueryFnData, @@ -559,7 +559,7 @@ export type EnsureInfiniteQueryDataOptions< TData = TQueryFnData, TQueryKey extends QueryKey = QueryKey, TPageParam = unknown, - TMode extends FetchPageDirectionMode = 'declarative', + TMode extends FetchPageDirectionMode = undefined, > = FetchInfiniteQueryOptions< TQueryFnData, TError, @@ -584,7 +584,7 @@ type FetchInfiniteQueryPagesDeclarative< type FetchInfiniteQueryPages< TQueryFnData = unknown, TPageParam = unknown, - TMode extends FetchPageDirectionMode = 'declarative', + TMode extends FetchPageDirectionMode = undefined, > = TMode extends InfiniteQueryMode ? { mode: InfiniteQueryMode @@ -600,7 +600,7 @@ export type FetchInfiniteQueryOptions< TData = TQueryFnData, TQueryKey extends QueryKey = QueryKey, TPageParam = unknown, - TMode extends FetchPageDirectionMode = 'declarative', + TMode extends FetchPageDirectionMode = undefined, > = Omit< FetchQueryOptions< TQueryFnData, @@ -668,28 +668,28 @@ export interface FetchPreviousPageOptions extends ResultOptions { export type InfiniteQueryFetchNextPageOptions< TPageParam = unknown, - TMode extends FetchPageDirectionMode = 'declarative', + TMode extends FetchPageDirectionMode = undefined, > = TMode extends InfiniteQueryMode ? ImperativeFetchPageOptions & FetchNextPageOptions : FetchNextPageOptions export type InfiniteQueryFetchPreviousPageOptions< TPageParam = unknown, - TMode extends FetchPageDirectionMode = 'declarative', + TMode extends FetchPageDirectionMode = undefined, > = TMode extends InfiniteQueryMode ? ImperativeFetchPageOptions & FetchPreviousPageOptions : FetchPreviousPageOptions export type InfiniteQueryFetchNextPageArgs< TPageParam = unknown, - TMode extends FetchPageDirectionMode = 'declarative', + TMode extends FetchPageDirectionMode = undefined, > = TMode extends InfiniteQueryMode ? [options: InfiniteQueryFetchNextPageOptions] : [options?: InfiniteQueryFetchNextPageOptions] export type InfiniteQueryFetchPreviousPageArgs< TPageParam = unknown, - TMode extends FetchPageDirectionMode = 'declarative', + TMode extends FetchPageDirectionMode = undefined, > = TMode extends InfiniteQueryMode ? [options: InfiniteQueryFetchPreviousPageOptions] : [options?: InfiniteQueryFetchPreviousPageOptions] @@ -987,7 +987,7 @@ export interface InfiniteQueryObserverBaseResult< TData = unknown, TError = DefaultError, TPageParam = unknown, - TMode extends FetchPageDirectionMode = 'declarative', + TMode extends FetchPageDirectionMode = undefined, > extends QueryObserverBaseResult { /** * This function allows you to fetch the next "page" of results. @@ -1031,7 +1031,7 @@ export interface InfiniteQueryObserverPendingResult< TData = unknown, TError = DefaultError, TPageParam = unknown, - TMode extends FetchPageDirectionMode = 'declarative', + TMode extends FetchPageDirectionMode = undefined, > extends InfiniteQueryObserverBaseResult { data: undefined error: null @@ -1050,7 +1050,7 @@ export interface InfiniteQueryObserverLoadingResult< TData = unknown, TError = DefaultError, TPageParam = unknown, - TMode extends FetchPageDirectionMode = 'declarative', + TMode extends FetchPageDirectionMode = undefined, > extends InfiniteQueryObserverBaseResult { data: undefined error: null @@ -1070,7 +1070,7 @@ export interface InfiniteQueryObserverLoadingErrorResult< TData = unknown, TError = DefaultError, TPageParam = unknown, - TMode extends FetchPageDirectionMode = 'declarative', + TMode extends FetchPageDirectionMode = undefined, > extends InfiniteQueryObserverBaseResult { data: undefined error: TError @@ -1090,7 +1090,7 @@ export interface InfiniteQueryObserverRefetchErrorResult< TData = unknown, TError = DefaultError, TPageParam = unknown, - TMode extends FetchPageDirectionMode = 'declarative', + TMode extends FetchPageDirectionMode = undefined, > extends InfiniteQueryObserverBaseResult { data: TData error: TError @@ -1108,7 +1108,7 @@ export interface InfiniteQueryObserverSuccessResult< TData = unknown, TError = DefaultError, TPageParam = unknown, - TMode extends FetchPageDirectionMode = 'declarative', + TMode extends FetchPageDirectionMode = undefined, > extends InfiniteQueryObserverBaseResult { data: TData error: null @@ -1128,7 +1128,7 @@ export interface InfiniteQueryObserverPlaceholderResult< TData = unknown, TError = DefaultError, TPageParam = unknown, - TMode extends FetchPageDirectionMode = 'declarative', + TMode extends FetchPageDirectionMode = undefined, > extends InfiniteQueryObserverBaseResult { data: TData isError: false @@ -1148,7 +1148,7 @@ export type DefinedInfiniteQueryObserverResult< TData = unknown, TError = DefaultError, TPageParam = unknown, - TMode extends FetchPageDirectionMode = 'declarative', + TMode extends FetchPageDirectionMode = undefined, > = | InfiniteQueryObserverRefetchErrorResult | InfiniteQueryObserverSuccessResult @@ -1157,7 +1157,7 @@ export type InfiniteQueryObserverResult< TData = unknown, TError = DefaultError, TPageParam = unknown, - TMode extends FetchPageDirectionMode = 'declarative', + TMode extends FetchPageDirectionMode = undefined, > = | DefinedInfiniteQueryObserverResult | InfiniteQueryObserverLoadingErrorResult diff --git a/packages/react-query/src/infiniteQueryOptions.ts b/packages/react-query/src/infiniteQueryOptions.ts index 640ddbf6fcf..53681c7e932 100644 --- a/packages/react-query/src/infiniteQueryOptions.ts +++ b/packages/react-query/src/infiniteQueryOptions.ts @@ -17,7 +17,7 @@ export type UndefinedInitialDataInfiniteOptions< TData = InfiniteData, TQueryKey extends QueryKey = QueryKey, TPageParam = unknown, - TMode extends FetchPageDirectionMode = 'declarative', + TMode extends FetchPageDirectionMode = undefined, > = UseInfiniteQueryOptions< TQueryFnData, TError, @@ -40,7 +40,7 @@ export type UnusedSkipTokenInfiniteOptions< TData = InfiniteData, TQueryKey extends QueryKey = QueryKey, TPageParam = unknown, - TMode extends FetchPageDirectionMode = 'declarative', + TMode extends FetchPageDirectionMode = undefined, > = OmitKeyof< UseInfiniteQueryOptions< TQueryFnData, @@ -71,7 +71,7 @@ export type DefinedInitialDataInfiniteOptions< TData = InfiniteData, TQueryKey extends QueryKey = QueryKey, TPageParam = unknown, - TMode extends FetchPageDirectionMode = 'declarative', + TMode extends FetchPageDirectionMode = undefined, > = UseInfiniteQueryOptions< TQueryFnData, TError, @@ -92,7 +92,7 @@ export function infiniteQueryOptions< TData = InfiniteData, TQueryKey extends QueryKey = QueryKey, TPageParam = unknown, - TMode extends FetchPageDirectionMode = 'declarative', + TMode extends FetchPageDirectionMode = undefined, >( options: DefinedInitialDataInfiniteOptions< TQueryFnData, @@ -119,7 +119,7 @@ export function infiniteQueryOptions< TData = InfiniteData, TQueryKey extends QueryKey = QueryKey, TPageParam = unknown, - TMode extends FetchPageDirectionMode = 'declarative', + TMode extends FetchPageDirectionMode = undefined, >( options: UnusedSkipTokenInfiniteOptions< TQueryFnData, @@ -146,7 +146,7 @@ export function infiniteQueryOptions< TData = InfiniteData, TQueryKey extends QueryKey = QueryKey, TPageParam = unknown, - TMode extends FetchPageDirectionMode = 'declarative', + TMode extends FetchPageDirectionMode = undefined, >( options: UndefinedInitialDataInfiniteOptions< TQueryFnData, diff --git a/packages/react-query/src/types.ts b/packages/react-query/src/types.ts index c7f0fbce368..e6086f457ce 100644 --- a/packages/react-query/src/types.ts +++ b/packages/react-query/src/types.ts @@ -107,7 +107,7 @@ export type UseInfiniteQueryOptions< TData = TQueryFnData, TQueryKey extends QueryKey = QueryKey, TPageParam = unknown, - TMode extends FetchPageDirectionMode = 'declarative', + TMode extends FetchPageDirectionMode = undefined, > = DistributiveOmit< InfiniteQueryObserverOptions< TQueryFnData, @@ -134,7 +134,7 @@ export type UseSuspenseInfiniteQueryOptions< TData = TQueryFnData, TQueryKey extends QueryKey = QueryKey, TPageParam = unknown, - TMode extends FetchPageDirectionMode = 'declarative', + TMode extends FetchPageDirectionMode = undefined, > = DistributiveOmit< UseInfiniteQueryOptions< TQueryFnData, diff --git a/packages/react-query/src/useInfiniteQuery.ts b/packages/react-query/src/useInfiniteQuery.ts index 113ce369dd5..99f69c413f1 100644 --- a/packages/react-query/src/useInfiniteQuery.ts +++ b/packages/react-query/src/useInfiniteQuery.ts @@ -25,7 +25,7 @@ export function useInfiniteQuery< TData = InfiniteData, TQueryKey extends QueryKey = QueryKey, TPageParam = unknown, - TMode extends FetchPageDirectionMode = 'declarative', + TMode extends FetchPageDirectionMode = undefined, >( options: DefinedInitialDataInfiniteOptions< TQueryFnData, @@ -44,7 +44,7 @@ export function useInfiniteQuery< TData = InfiniteData, TQueryKey extends QueryKey = QueryKey, TPageParam = unknown, - TMode extends FetchPageDirectionMode = 'declarative', + TMode extends FetchPageDirectionMode = undefined, >( options: UndefinedInitialDataInfiniteOptions< TQueryFnData, @@ -63,7 +63,7 @@ export function useInfiniteQuery< TData = InfiniteData, TQueryKey extends QueryKey = QueryKey, TPageParam = unknown, - TMode extends FetchPageDirectionMode = 'declarative', + TMode extends FetchPageDirectionMode = undefined, >( options: UseInfiniteQueryOptions< TQueryFnData, From adf09b0163798a3eb098d580b505d2970d8b4cf1 Mon Sep 17 00:00:00 2001 From: TkDodo Date: Fri, 3 Apr 2026 15:52:03 +0200 Subject: [PATCH 06/25] remove move imperative from runtime --- .../query-core/src/infiniteQueryBehavior.ts | 37 +++++-------------- packages/query-core/src/queryClient.ts | 6 +-- 2 files changed, 12 insertions(+), 31 deletions(-) diff --git a/packages/query-core/src/infiniteQueryBehavior.ts b/packages/query-core/src/infiniteQueryBehavior.ts index 0bc088b06fe..8d22888cfa8 100644 --- a/packages/query-core/src/infiniteQueryBehavior.ts +++ b/packages/query-core/src/infiniteQueryBehavior.ts @@ -7,28 +7,20 @@ import { import type { QueryBehavior } from './query' import type { InfiniteData, - InfiniteQueryMode, InfiniteQueryPageParamsOptions, OmitKeyof, QueryFunctionContext, QueryKey, } from './types' -export function infiniteQueryBehavior< - TQueryFnData, - TError, - TData, - TPageParam, - TMode extends InfiniteQueryMode | undefined = undefined, ->( +export function infiniteQueryBehavior( pages?: number, ): QueryBehavior> { return { onFetch: (context, query) => { const options = context.options as InfiniteQueryPageParamsOptions< TData, - TPageParam, - TMode + TPageParam > const fetchMore = context.fetchOptions?.meta?.fetchMore const oldPages = context.state.data?.pages || [] @@ -99,19 +91,18 @@ export function infiniteQueryBehavior< pageParams: oldPageParams, } const param = - options.mode === 'imperative' || fetchMore.pageParam === undefined - ? fetchMore.pageParam - : pageParamFn(options, oldData) + fetchMore.pageParam === undefined + ? pageParamFn(options, oldData) + : fetchMore.pageParam result = await fetchPage(oldData, param, previous) } else { - const remainingPages = - options.mode === 'imperative' ? 1 : (pages ?? oldPages.length) + const remainingPages = pages ?? Math.max(oldPages.length, 1) // Fetch all pages do { const param = - currentPage === 0 || !options.getNextPageParam + currentPage === 0 ? (oldPageParams[currentPage] ?? options.initialPageParam) : getNextPageParam(options, result) if (currentPage > 0 && param == null) { @@ -148,13 +139,9 @@ function getNextPageParam( options: InfiniteQueryPageParamsOptions, { pages, pageParams }: InfiniteData, ): unknown | undefined { - if (options.mode === 'imperative') { - return undefined - } - const lastIndex = pages.length - 1 return pages.length > 0 - ? options.getNextPageParam( + ? options.getNextPageParam?.( pages[lastIndex], pages, pageParams[lastIndex], @@ -167,10 +154,6 @@ function getPreviousPageParam( options: InfiniteQueryPageParamsOptions, { pages, pageParams }: InfiniteData, ): unknown | undefined { - if (options.mode === 'imperative') { - return undefined - } - return pages.length > 0 ? options.getPreviousPageParam?.(pages[0], pages, pageParams[0], pageParams) : undefined @@ -183,7 +166,7 @@ export function hasNextPage( options: InfiniteQueryPageParamsOptions, data?: InfiniteData, ): boolean { - if (!data || options.mode === 'imperative') return false + if (!data) return false return getNextPageParam(options, data) != null } @@ -194,7 +177,7 @@ export function hasPreviousPage( options: InfiniteQueryPageParamsOptions, data?: InfiniteData, ): boolean { - if (!data || options.mode === 'imperative' || !options.getPreviousPageParam) { + if (!data || !options.getPreviousPageParam) { return false } return getPreviousPageParam(options, data) != null diff --git a/packages/query-core/src/queryClient.ts b/packages/query-core/src/queryClient.ts index fb13964a307..a6d25bf8a8c 100644 --- a/packages/query-core/src/queryClient.ts +++ b/packages/query-core/src/queryClient.ts @@ -434,8 +434,7 @@ export class QueryClient { TQueryFnData, TError, TData, - TPageParam, - TMode + TPageParam >(options.pages) return this.fetchQuery(options as any) } @@ -545,8 +544,7 @@ export class QueryClient { TQueryFnData, TError, TData, - TPageParam, - TMode + TPageParam >(options.pages) return this.ensureQueryData(options as any) From 778e0f0a6071adaf7af069812fe9a9f2bc7a62d5 Mon Sep 17 00:00:00 2001 From: TkDodo Date: Fri, 3 Apr 2026 15:57:14 +0200 Subject: [PATCH 07/25] fix: bring back options check --- packages/query-core/src/infiniteQueryBehavior.ts | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/packages/query-core/src/infiniteQueryBehavior.ts b/packages/query-core/src/infiniteQueryBehavior.ts index 8d22888cfa8..a56c25085bf 100644 --- a/packages/query-core/src/infiniteQueryBehavior.ts +++ b/packages/query-core/src/infiniteQueryBehavior.ts @@ -4,24 +4,25 @@ import { addToStart, ensureQueryFn, } from './utils' -import type { QueryBehavior } from './query' import type { InfiniteData, + InfiniteQueryPageParamsDeclarativeOptions, + InfiniteQueryPageParamsImperativeOptions, InfiniteQueryPageParamsOptions, OmitKeyof, QueryFunctionContext, QueryKey, } from './types' +import type { QueryBehavior } from './query' export function infiniteQueryBehavior( pages?: number, ): QueryBehavior> { return { onFetch: (context, query) => { - const options = context.options as InfiniteQueryPageParamsOptions< - TData, - TPageParam - > + const options = context.options as + | InfiniteQueryPageParamsImperativeOptions + | InfiniteQueryPageParamsDeclarativeOptions const fetchMore = context.fetchOptions?.meta?.fetchMore const oldPages = context.state.data?.pages || [] const oldPageParams = context.state.data?.pageParams || [] @@ -102,7 +103,7 @@ export function infiniteQueryBehavior( // Fetch all pages do { const param = - currentPage === 0 + currentPage === 0 || !options.getNextPageParam ? (oldPageParams[currentPage] ?? options.initialPageParam) : getNextPageParam(options, result) if (currentPage > 0 && param == null) { From 1a6fe02c511a8802c9f7bb7aee0494da4eca0e70 Mon Sep 17 00:00:00 2001 From: TkDodo Date: Fri, 3 Apr 2026 16:02:56 +0200 Subject: [PATCH 08/25] fix runtime error --- .../query-core/src/infiniteQueryObserver.ts | 39 ++++++++++--------- 1 file changed, 20 insertions(+), 19 deletions(-) diff --git a/packages/query-core/src/infiniteQueryObserver.ts b/packages/query-core/src/infiniteQueryObserver.ts index c01d1160afa..335edd2f88e 100644 --- a/packages/query-core/src/infiniteQueryObserver.ts +++ b/packages/query-core/src/infiniteQueryObserver.ts @@ -18,8 +18,8 @@ import type { InfiniteQueryObserverBaseResult, InfiniteQueryObserverOptions, InfiniteQueryObserverResult, - QueryObserverOptions, QueryKey, + QueryObserverOptions, } from './types' import type { QueryClient } from './queryClient' import type { Query } from './query' @@ -52,14 +52,6 @@ export class InfiniteQueryObserver< InfiniteQueryObserverListener >['subscribe'] - fetchNextPage!: ( - ...args: InfiniteQueryFetchNextPageArgs - ) => Promise> - - fetchPreviousPage!: ( - ...args: InfiniteQueryFetchPreviousPageArgs - ) => Promise> - // Type override getCurrentResult!: ReplaceReturnType< QueryObserver< @@ -122,10 +114,8 @@ export class InfiniteQueryObserver< protected bindMethods(): void { super.bindMethods() - this.fetchNextPage = - this.#fetchNextPage.bind(this) as typeof this.fetchNextPage - this.fetchPreviousPage = - this.#fetchPreviousPage.bind(this) as typeof this.fetchPreviousPage + this.fetchNextPage = this.fetchNextPage.bind(this) + this.fetchPreviousPage = this.fetchPreviousPage.bind(this) } setOptions( @@ -168,7 +158,10 @@ export class InfiniteQueryObserver< > } - #fetchNextPage( + fetchNextPage( + ...args: InfiniteQueryFetchNextPageArgs + ): Promise> + fetchNextPage( options?: InfiniteQueryFetchNextPageOptions, ): Promise> { const pageParam = (options as { pageParam?: TPageParam } | undefined) @@ -186,7 +179,10 @@ export class InfiniteQueryObserver< }) } - #fetchPreviousPage( + fetchPreviousPage( + ...args: InfiniteQueryFetchPreviousPageArgs + ): Promise> + fetchPreviousPage( options?: InfiniteQueryFetchPreviousPageOptions, ): Promise> { const pageParam = (options as { pageParam?: TPageParam } | undefined) @@ -219,7 +215,7 @@ export class InfiniteQueryObserver< TQueryKey >, ): InfiniteQueryObserverResult { - return this.#createResult( + return this.createInfiniteResult( query, options as unknown as InfiniteQueryObserverOptions< TQueryFnData, @@ -231,8 +227,8 @@ export class InfiniteQueryObserver< >, ) } - - #createResult( + + private createInfiniteResult( query: Query< TQueryFnData, TError, @@ -290,7 +286,12 @@ export class InfiniteQueryObserver< isRefetching && !isFetchingNextPage && !isFetchingPreviousPage, } - return result as InfiniteQueryObserverResult + return result as InfiniteQueryObserverResult< + TData, + TError, + TPageParam, + TMode + > } } From 4e4c33d51a325aab8c1822e210be5970509ad466 Mon Sep 17 00:00:00 2001 From: TkDodo Date: Fri, 3 Apr 2026 16:05:54 +0200 Subject: [PATCH 09/25] simplify --- .../query-core/src/infiniteQueryObserver.ts | 26 +++++-------------- 1 file changed, 6 insertions(+), 20 deletions(-) diff --git a/packages/query-core/src/infiniteQueryObserver.ts b/packages/query-core/src/infiniteQueryObserver.ts index 335edd2f88e..16727e2e080 100644 --- a/packages/query-core/src/infiniteQueryObserver.ts +++ b/packages/query-core/src/infiniteQueryObserver.ts @@ -11,9 +11,7 @@ import type { FetchPageDirectionMode, InfiniteData, InfiniteQueryFetchNextPageArgs, - InfiniteQueryFetchNextPageOptions, InfiniteQueryFetchPreviousPageArgs, - InfiniteQueryFetchPreviousPageOptions, InfiniteQueryMode, InfiniteQueryObserverBaseResult, InfiniteQueryObserverOptions, @@ -162,17 +160,11 @@ export class InfiniteQueryObserver< ...args: InfiniteQueryFetchNextPageArgs ): Promise> fetchNextPage( - options?: InfiniteQueryFetchNextPageOptions, + ...args: Array ): Promise> { - const pageParam = (options as { pageParam?: TPageParam } | undefined) - ?.pageParam - const fetchOptions = { ...(options ?? {}) } as Record - delete fetchOptions.pageParam + const [{ pageParam, ...options } = {}] = args return this.fetch({ - ...(fetchOptions as unknown as InfiniteQueryFetchNextPageOptions< - TPageParam, - TMode - >), + ...options, meta: { fetchMore: { direction: 'forward', pageParam }, }, @@ -183,17 +175,11 @@ export class InfiniteQueryObserver< ...args: InfiniteQueryFetchPreviousPageArgs ): Promise> fetchPreviousPage( - options?: InfiniteQueryFetchPreviousPageOptions, + ...args: Array ): Promise> { - const pageParam = (options as { pageParam?: TPageParam } | undefined) - ?.pageParam - const fetchOptions = { ...(options ?? {}) } as Record - delete fetchOptions.pageParam + const [{ pageParam, ...options } = {}] = args return this.fetch({ - ...(fetchOptions as unknown as InfiniteQueryFetchPreviousPageOptions< - TPageParam, - TMode - >), + ...options, meta: { fetchMore: { direction: 'backward', pageParam }, }, From b326f4ec726fba5aa383d0807cc089cf7e6396f0 Mon Sep 17 00:00:00 2001 From: TkDodo Date: Fri, 3 Apr 2026 16:17:19 +0200 Subject: [PATCH 10/25] fix react layer --- .../__tests__/infiniteQueryOptions.test-d.tsx | 4 +- .../src/__tests__/useInfiniteQuery.test-d.tsx | 21 ++ .../react-query/src/infiniteQueryOptions.ts | 312 ++++++++++++++++-- packages/react-query/src/types.ts | 145 ++++++-- packages/react-query/src/useInfiniteQuery.ts | 49 ++- 5 files changed, 444 insertions(+), 87 deletions(-) diff --git a/packages/react-query/src/__tests__/infiniteQueryOptions.test-d.tsx b/packages/react-query/src/__tests__/infiniteQueryOptions.test-d.tsx index ec54f5f1e7c..67d709e4394 100644 --- a/packages/react-query/src/__tests__/infiniteQueryOptions.test-d.tsx +++ b/packages/react-query/src/__tests__/infiniteQueryOptions.test-d.tsx @@ -166,21 +166,21 @@ describe('infiniteQueryOptions', () => { initialPageParam: 1, }) + // @ts-expect-error getNextPageParam is not allowed in imperative mode infiniteQueryOptions({ queryKey: ['key'], queryFn: () => Promise.resolve('string'), initialPageParam: 1, mode: 'imperative', - // @ts-expect-error getNextPageParam is not allowed in imperative mode getNextPageParam: () => 1, }) + // @ts-expect-error getPreviousPageParam is not allowed in imperative mode infiniteQueryOptions({ queryKey: ['key'], queryFn: () => Promise.resolve('string'), initialPageParam: 1, mode: 'imperative', - // @ts-expect-error getPreviousPageParam is not allowed in imperative mode getPreviousPageParam: () => 0, }) }) diff --git a/packages/react-query/src/__tests__/useInfiniteQuery.test-d.tsx b/packages/react-query/src/__tests__/useInfiniteQuery.test-d.tsx index ea55f2515a9..6420b4c9579 100644 --- a/packages/react-query/src/__tests__/useInfiniteQuery.test-d.tsx +++ b/packages/react-query/src/__tests__/useInfiniteQuery.test-d.tsx @@ -136,6 +136,27 @@ describe('getNextPageParam / getPreviousPageParam', () => { InfiniteData | undefined >() }) + + it('should infer async object page types for getNextPageParam', () => { + useInfiniteQuery({ + queryKey: ['key'], + queryFn: async ({ pageParam = 0 }) => { + return { + nextCursor: pageParam + 1, + data: `page-${pageParam}`, + } + }, + initialPageParam: 0, + getNextPageParam: (lastPage) => { + expectTypeOf(lastPage).toEqualTypeOf<{ + nextCursor: number + data: string + }>() + return lastPage.nextCursor + }, + retry: false, + }) + }) }) describe('error booleans', () => { diff --git a/packages/react-query/src/infiniteQueryOptions.ts b/packages/react-query/src/infiniteQueryOptions.ts index 53681c7e932..b61fc6949ef 100644 --- a/packages/react-query/src/infiniteQueryOptions.ts +++ b/packages/react-query/src/infiniteQueryOptions.ts @@ -1,7 +1,6 @@ import type { DataTag, DefaultError, - FetchPageDirectionMode, InfiniteData, InitialDataFunction, NonUndefinedGuard, @@ -9,7 +8,11 @@ import type { QueryKey, SkipToken, } from '@tanstack/query-core' -import type { UseInfiniteQueryOptions } from './types' +import type { + UseDeclarativeInfiniteQueryOptions, + UseImperativeInfiniteQueryOptions, + UseInfiniteQueryOptions, +} from './types' export type UndefinedInitialDataInfiniteOptions< TQueryFnData, @@ -17,14 +20,12 @@ export type UndefinedInitialDataInfiniteOptions< TData = InfiniteData, TQueryKey extends QueryKey = QueryKey, TPageParam = unknown, - TMode extends FetchPageDirectionMode = undefined, > = UseInfiniteQueryOptions< TQueryFnData, TError, TData, TQueryKey, - TPageParam, - TMode + TPageParam > & { initialData?: | undefined @@ -34,51 +35,179 @@ export type UndefinedInitialDataInfiniteOptions< > } -export type UnusedSkipTokenInfiniteOptions< +export type DeclarativeUndefinedInitialDataInfiniteOptions< + TQueryFnData, + TError = DefaultError, + TData = InfiniteData, + TQueryKey extends QueryKey = QueryKey, + TPageParam = unknown, +> = UseDeclarativeInfiniteQueryOptions< + TQueryFnData, + TError, + TData, + TQueryKey, + TPageParam +> & { + initialData?: + | undefined + | NonUndefinedGuard> + | InitialDataFunction< + NonUndefinedGuard> + > +} + +export type ImperativeUndefinedInitialDataInfiniteOptions< + TQueryFnData, + TError = DefaultError, + TData = InfiniteData, + TQueryKey extends QueryKey = QueryKey, + TPageParam = unknown, +> = UseImperativeInfiniteQueryOptions< + TQueryFnData, + TError, + TData, + TQueryKey, + TPageParam +> & { + initialData?: + | undefined + | NonUndefinedGuard> + | InitialDataFunction< + NonUndefinedGuard> + > +} + +export type UnusedSkipTokenDeclarativeInfiniteOptions< + TQueryFnData, + TError = DefaultError, + TData = InfiniteData, + TQueryKey extends QueryKey = QueryKey, + TPageParam = unknown, +> = OmitKeyof< + UseDeclarativeInfiniteQueryOptions< + TQueryFnData, + TError, + TData, + TQueryKey, + TPageParam + >, + 'queryFn' +> & { + queryFn?: Exclude< + UseDeclarativeInfiniteQueryOptions< + TQueryFnData, + TError, + TData, + TQueryKey, + TPageParam + >['queryFn'], + SkipToken | undefined + > +} + +export type UnusedSkipTokenImperativeInfiniteOptions< TQueryFnData, TError = DefaultError, TData = InfiniteData, TQueryKey extends QueryKey = QueryKey, TPageParam = unknown, - TMode extends FetchPageDirectionMode = undefined, > = OmitKeyof< - UseInfiniteQueryOptions< + UseImperativeInfiniteQueryOptions< TQueryFnData, TError, TData, TQueryKey, - TPageParam, - TMode + TPageParam >, 'queryFn' > & { queryFn?: Exclude< - UseInfiniteQueryOptions< + UseImperativeInfiniteQueryOptions< TQueryFnData, TError, TData, TQueryKey, - TPageParam, - TMode + TPageParam >['queryFn'], SkipToken | undefined > } +export type UnusedSkipTokenInfiniteOptions< + TQueryFnData, + TError = DefaultError, + TData = InfiniteData, + TQueryKey extends QueryKey = QueryKey, + TPageParam = unknown, +> = + | UnusedSkipTokenDeclarativeInfiniteOptions< + TQueryFnData, + TError, + TData, + TQueryKey, + TPageParam + > + | UnusedSkipTokenImperativeInfiniteOptions< + TQueryFnData, + TError, + TData, + TQueryKey, + TPageParam + > + export type DefinedInitialDataInfiniteOptions< TQueryFnData, TError = DefaultError, TData = InfiniteData, TQueryKey extends QueryKey = QueryKey, TPageParam = unknown, - TMode extends FetchPageDirectionMode = undefined, -> = UseInfiniteQueryOptions< +> = + | DeclarativeDefinedInitialDataInfiniteOptions< + TQueryFnData, + TError, + TData, + TQueryKey, + TPageParam + > + | ImperativeDefinedInitialDataInfiniteOptions< + TQueryFnData, + TError, + TData, + TQueryKey, + TPageParam + > + +export type DeclarativeDefinedInitialDataInfiniteOptions< + TQueryFnData, + TError = DefaultError, + TData = InfiniteData, + TQueryKey extends QueryKey = QueryKey, + TPageParam = unknown, +> = UseDeclarativeInfiniteQueryOptions< TQueryFnData, TError, TData, TQueryKey, - TPageParam, - TMode + TPageParam +> & { + initialData: + | NonUndefinedGuard> + | (() => NonUndefinedGuard>) + | undefined +} + +export type ImperativeDefinedInitialDataInfiniteOptions< + TQueryFnData, + TError = DefaultError, + TData = InfiniteData, + TQueryKey extends QueryKey = QueryKey, + TPageParam = unknown, +> = UseImperativeInfiniteQueryOptions< + TQueryFnData, + TError, + TData, + TQueryKey, + TPageParam > & { initialData: | NonUndefinedGuard> @@ -92,23 +221,20 @@ export function infiniteQueryOptions< TData = InfiniteData, TQueryKey extends QueryKey = QueryKey, TPageParam = unknown, - TMode extends FetchPageDirectionMode = undefined, >( - options: DefinedInitialDataInfiniteOptions< + options: DeclarativeDefinedInitialDataInfiniteOptions< TQueryFnData, TError, TData, TQueryKey, - TPageParam, - TMode + TPageParam >, -): DefinedInitialDataInfiniteOptions< +): DeclarativeDefinedInitialDataInfiniteOptions< TQueryFnData, TError, TData, TQueryKey, - TPageParam, - TMode + TPageParam > & { queryKey: DataTag, TError> } @@ -119,23 +245,140 @@ export function infiniteQueryOptions< TData = InfiniteData, TQueryKey extends QueryKey = QueryKey, TPageParam = unknown, - TMode extends FetchPageDirectionMode = undefined, >( - options: UnusedSkipTokenInfiniteOptions< + options: ImperativeDefinedInitialDataInfiniteOptions< TQueryFnData, TError, TData, TQueryKey, - TPageParam, - TMode + TPageParam >, -): UnusedSkipTokenInfiniteOptions< +): ImperativeDefinedInitialDataInfiniteOptions< + TQueryFnData, + TError, + TData, + TQueryKey, + TPageParam +> & { + queryKey: DataTag, TError> +} + +export function infiniteQueryOptions< + TQueryFnData, + TError = DefaultError, + TData = InfiniteData, + TQueryKey extends QueryKey = QueryKey, + TPageParam = unknown, +>( + options: UnusedSkipTokenDeclarativeInfiniteOptions< + TQueryFnData, + TError, + TData, + TQueryKey, + TPageParam + >, +): UnusedSkipTokenDeclarativeInfiniteOptions< + TQueryFnData, + TError, + TData, + TQueryKey, + TPageParam +> & { + queryKey: DataTag, TError> +} + +export function infiniteQueryOptions< + TQueryFnData, + TError = DefaultError, + TData = InfiniteData, + TQueryKey extends QueryKey = QueryKey, + TPageParam = unknown, +>( + options: UnusedSkipTokenImperativeInfiniteOptions< + TQueryFnData, + TError, + TData, + TQueryKey, + TPageParam + >, +): UnusedSkipTokenImperativeInfiniteOptions< + TQueryFnData, + TError, + TData, + TQueryKey, + TPageParam +> & { + queryKey: DataTag, TError> +} + +export function infiniteQueryOptions< + TQueryFnData, + TError = DefaultError, + TData = InfiniteData, + TQueryKey extends QueryKey = QueryKey, + TPageParam = unknown, +>( + options: DeclarativeUndefinedInitialDataInfiniteOptions< + TQueryFnData, + TError, + TData, + TQueryKey, + TPageParam + >, +): DeclarativeUndefinedInitialDataInfiniteOptions< + TQueryFnData, + TError, + TData, + TQueryKey, + TPageParam +> & { + queryKey: DataTag, TError> +} + +export function infiniteQueryOptions< + TQueryFnData, + TError = DefaultError, + TData = InfiniteData, + TQueryKey extends QueryKey = QueryKey, + TPageParam = unknown, +>( + options: ImperativeUndefinedInitialDataInfiniteOptions< + TQueryFnData, + TError, + TData, + TQueryKey, + TPageParam + >, +): ImperativeUndefinedInitialDataInfiniteOptions< + TQueryFnData, + TError, + TData, + TQueryKey, + TPageParam +> & { + queryKey: DataTag, TError> +} + +export function infiniteQueryOptions< + TQueryFnData, + TError = DefaultError, + TData = InfiniteData, + TQueryKey extends QueryKey = QueryKey, + TPageParam = unknown, +>( + options: DefinedInitialDataInfiniteOptions< + TQueryFnData, + TError, + TData, + TQueryKey, + TPageParam + >, +): DefinedInitialDataInfiniteOptions< TQueryFnData, TError, TData, TQueryKey, - TPageParam, - TMode + TPageParam > & { queryKey: DataTag, TError> } @@ -146,23 +389,20 @@ export function infiniteQueryOptions< TData = InfiniteData, TQueryKey extends QueryKey = QueryKey, TPageParam = unknown, - TMode extends FetchPageDirectionMode = undefined, >( options: UndefinedInitialDataInfiniteOptions< TQueryFnData, TError, TData, TQueryKey, - TPageParam, - TMode + TPageParam >, ): UndefinedInitialDataInfiniteOptions< TQueryFnData, TError, TData, TQueryKey, - TPageParam, - TMode + TPageParam > & { queryKey: DataTag, TError> } diff --git a/packages/react-query/src/types.ts b/packages/react-query/src/types.ts index e6086f457ce..e504062f5c4 100644 --- a/packages/react-query/src/types.ts +++ b/packages/react-query/src/types.ts @@ -5,8 +5,8 @@ import type { DefinedInfiniteQueryObserverResult, DefinedQueryObserverResult, DistributiveOmit, - FetchPageDirectionMode, FetchQueryOptions, + InfiniteQueryMode, InfiniteQueryObserverOptions, InfiniteQueryObserverResult, MutateFunction, @@ -101,13 +101,13 @@ export type AnyUseInfiniteQueryOptions = UseInfiniteQueryOptions< any, any > -export type UseInfiniteQueryOptions< +type UseInfiniteQueryOptionsBase< TQueryFnData = unknown, TError = DefaultError, TData = TQueryFnData, TQueryKey extends QueryKey = QueryKey, TPageParam = unknown, - TMode extends FetchPageDirectionMode = undefined, + TMode extends InfiniteQueryMode | undefined = undefined, > = DistributiveOmit< InfiniteQueryObserverOptions< TQueryFnData, @@ -119,44 +119,121 @@ export type UseInfiniteQueryOptions< >, 'suspense' > & { - /** - * Set this to `false` to unsubscribe this observer from updates to the query cache. - * Defaults to `true`. - */ subscribed?: boolean } -export type AnyUseSuspenseInfiniteQueryOptions = - UseSuspenseInfiniteQueryOptions -export type UseSuspenseInfiniteQueryOptions< +export type UseDeclarativeInfiniteQueryOptions< TQueryFnData = unknown, TError = DefaultError, TData = TQueryFnData, TQueryKey extends QueryKey = QueryKey, TPageParam = unknown, - TMode extends FetchPageDirectionMode = undefined, -> = DistributiveOmit< - UseInfiniteQueryOptions< - TQueryFnData, - TError, - TData, - TQueryKey, - TPageParam, - TMode - >, - 'queryFn' | 'enabled' | 'throwOnError' | 'placeholderData' -> & { - queryFn?: Exclude< - UseInfiniteQueryOptions< +> = UseInfiniteQueryOptionsBase< + TQueryFnData, + TError, + TData, + TQueryKey, + TPageParam, + undefined +> + +export type UseImperativeInfiniteQueryOptions< + TQueryFnData = unknown, + TError = DefaultError, + TData = TQueryFnData, + TQueryKey extends QueryKey = QueryKey, + TPageParam = unknown, +> = UseInfiniteQueryOptionsBase< + TQueryFnData, + TError, + TData, + TQueryKey, + TPageParam, + InfiniteQueryMode +> + +export type UseInfiniteQueryOptions< + TQueryFnData = unknown, + TError = DefaultError, + TData = TQueryFnData, + TQueryKey extends QueryKey = QueryKey, + TPageParam = unknown, +> = + | UseDeclarativeInfiniteQueryOptions< TQueryFnData, TError, TData, TQueryKey, TPageParam - >['queryFn'], - SkipToken - > -} + > + | UseImperativeInfiniteQueryOptions< + TQueryFnData, + TError, + TData, + TQueryKey, + TPageParam + > + +export type UseSuspenseInfiniteQueryOptions< + TQueryFnData = unknown, + TError = DefaultError, + TData = TQueryFnData, + TQueryKey extends QueryKey = QueryKey, + TPageParam = unknown, +> = + | (DistributiveOmit< + UseDeclarativeInfiniteQueryOptions< + TQueryFnData, + TError, + TData, + TQueryKey, + TPageParam + >, + 'queryFn' | 'enabled' | 'throwOnError' | 'placeholderData' + > & { + /** + * Set this to `false` to unsubscribe this observer from updates to the query cache. + * Defaults to `true`. + */ + queryFn?: Exclude< + UseDeclarativeInfiniteQueryOptions< + TQueryFnData, + TError, + TData, + TQueryKey, + TPageParam + >['queryFn'], + SkipToken + > + }) + | (DistributiveOmit< + UseImperativeInfiniteQueryOptions< + TQueryFnData, + TError, + TData, + TQueryKey, + TPageParam + >, + 'queryFn' | 'enabled' | 'throwOnError' | 'placeholderData' + > & { + /** + * Set this to `false` to unsubscribe this observer from updates to the query cache. + * Defaults to `true`. + */ + queryFn?: Exclude< + UseImperativeInfiniteQueryOptions< + TQueryFnData, + TError, + TData, + TQueryKey, + TPageParam + >['queryFn'], + SkipToken + > + }) + +export type AnyUseSuspenseInfiniteQueryOptions = + UseSuspenseInfiniteQueryOptions export type UseBaseQueryResult< TData = unknown, @@ -184,18 +261,24 @@ export type DefinedUseQueryResult< export type UseInfiniteQueryResult< TData = unknown, TError = DefaultError, -> = InfiniteQueryObserverResult + TPageParam = unknown, + TMode extends InfiniteQueryMode | undefined = undefined, +> = InfiniteQueryObserverResult export type DefinedUseInfiniteQueryResult< TData = unknown, TError = DefaultError, -> = DefinedInfiniteQueryObserverResult + TPageParam = unknown, + TMode extends InfiniteQueryMode | undefined = undefined, +> = DefinedInfiniteQueryObserverResult export type UseSuspenseInfiniteQueryResult< TData = unknown, TError = DefaultError, + TPageParam = unknown, + TMode extends InfiniteQueryMode | undefined = undefined, > = OmitKeyof< - DefinedInfiniteQueryObserverResult, + DefinedInfiniteQueryObserverResult, 'isPlaceholderData' | 'promise' > diff --git a/packages/react-query/src/useInfiniteQuery.ts b/packages/react-query/src/useInfiniteQuery.ts index 99f69c413f1..7dc240baf77 100644 --- a/packages/react-query/src/useInfiniteQuery.ts +++ b/packages/react-query/src/useInfiniteQuery.ts @@ -3,8 +3,8 @@ import { InfiniteQueryObserver } from '@tanstack/query-core' import { useBaseQuery } from './useBaseQuery' import type { DefaultError, - FetchPageDirectionMode, InfiniteData, + InfiniteQueryMode, QueryClient, QueryKey, QueryObserver, @@ -15,8 +15,10 @@ import type { UseInfiniteQueryResult, } from './types' import type { - DefinedInitialDataInfiniteOptions, - UndefinedInitialDataInfiniteOptions, + DeclarativeDefinedInitialDataInfiniteOptions, + DeclarativeUndefinedInitialDataInfiniteOptions, + ImperativeDefinedInitialDataInfiniteOptions, + ImperativeUndefinedInitialDataInfiniteOptions, } from './infiniteQueryOptions' export function useInfiniteQuery< @@ -25,18 +27,16 @@ export function useInfiniteQuery< TData = InfiniteData, TQueryKey extends QueryKey = QueryKey, TPageParam = unknown, - TMode extends FetchPageDirectionMode = undefined, >( - options: DefinedInitialDataInfiniteOptions< + options: DeclarativeDefinedInitialDataInfiniteOptions< TQueryFnData, TError, TData, TQueryKey, - TPageParam, - TMode + TPageParam >, queryClient?: QueryClient, -): DefinedUseInfiniteQueryResult +): DefinedUseInfiniteQueryResult export function useInfiniteQuery< TQueryFnData, @@ -44,18 +44,16 @@ export function useInfiniteQuery< TData = InfiniteData, TQueryKey extends QueryKey = QueryKey, TPageParam = unknown, - TMode extends FetchPageDirectionMode = undefined, >( - options: UndefinedInitialDataInfiniteOptions< + options: ImperativeDefinedInitialDataInfiniteOptions< TQueryFnData, TError, TData, TQueryKey, - TPageParam, - TMode + TPageParam >, queryClient?: QueryClient, -): UseInfiniteQueryResult +): DefinedUseInfiniteQueryResult export function useInfiniteQuery< TQueryFnData, @@ -63,18 +61,33 @@ export function useInfiniteQuery< TData = InfiniteData, TQueryKey extends QueryKey = QueryKey, TPageParam = unknown, - TMode extends FetchPageDirectionMode = undefined, >( - options: UseInfiniteQueryOptions< + options: DeclarativeUndefinedInitialDataInfiniteOptions< TQueryFnData, TError, TData, TQueryKey, - TPageParam, - TMode + TPageParam >, queryClient?: QueryClient, -): UseInfiniteQueryResult +): UseInfiniteQueryResult + +export function useInfiniteQuery< + TQueryFnData, + TError = DefaultError, + TData = InfiniteData, + TQueryKey extends QueryKey = QueryKey, + TPageParam = unknown, +>( + options: ImperativeUndefinedInitialDataInfiniteOptions< + TQueryFnData, + TError, + TData, + TQueryKey, + TPageParam + >, + queryClient?: QueryClient, +): UseInfiniteQueryResult export function useInfiniteQuery( options: UseInfiniteQueryOptions, From 2a949846135d04aed9e929c2db1417362bac0a54 Mon Sep 17 00:00:00 2001 From: TkDodo Date: Fri, 3 Apr 2026 16:24:38 +0200 Subject: [PATCH 11/25] "simplify" infiniteQueryOptions --- .../react-query/src/infiniteQueryOptions.ts | 156 ++++++++---------- 1 file changed, 68 insertions(+), 88 deletions(-) diff --git a/packages/react-query/src/infiniteQueryOptions.ts b/packages/react-query/src/infiniteQueryOptions.ts index b61fc6949ef..6124e709b8b 100644 --- a/packages/react-query/src/infiniteQueryOptions.ts +++ b/packages/react-query/src/infiniteQueryOptions.ts @@ -14,6 +14,44 @@ import type { UseInfiniteQueryOptions, } from './types' +type OptionalInitialData< + TQueryFnData, + TPageParam, +> = { + initialData?: + | undefined + | NonUndefinedGuard> + | InitialDataFunction< + NonUndefinedGuard> + > +} + +type RequiredInitialData< + TQueryFnData, + TPageParam, +> = { + initialData: + | NonUndefinedGuard> + | (() => NonUndefinedGuard>) + | undefined +} + +type WithoutSkipTokenQueryFn = OmitKeyof< + TOptions, + 'queryFn' +> & { + queryFn?: Exclude +} + +type TaggedInfiniteQueryOptions< + TOptions, + TQueryKey extends QueryKey, + TQueryFnData, + TError, +> = TOptions & { + queryKey: DataTag, TError> +} + export type UndefinedInitialDataInfiniteOptions< TQueryFnData, TError = DefaultError, @@ -26,14 +64,8 @@ export type UndefinedInitialDataInfiniteOptions< TData, TQueryKey, TPageParam -> & { - initialData?: - | undefined - | NonUndefinedGuard> - | InitialDataFunction< - NonUndefinedGuard> - > -} +> & + OptionalInitialData export type DeclarativeUndefinedInitialDataInfiniteOptions< TQueryFnData, @@ -47,14 +79,8 @@ export type DeclarativeUndefinedInitialDataInfiniteOptions< TData, TQueryKey, TPageParam -> & { - initialData?: - | undefined - | NonUndefinedGuard> - | InitialDataFunction< - NonUndefinedGuard> - > -} +> & + OptionalInitialData export type ImperativeUndefinedInitialDataInfiniteOptions< TQueryFnData, @@ -68,14 +94,8 @@ export type ImperativeUndefinedInitialDataInfiniteOptions< TData, TQueryKey, TPageParam -> & { - initialData?: - | undefined - | NonUndefinedGuard> - | InitialDataFunction< - NonUndefinedGuard> - > -} +> & + OptionalInitialData export type UnusedSkipTokenDeclarativeInfiniteOptions< TQueryFnData, @@ -83,27 +103,15 @@ export type UnusedSkipTokenDeclarativeInfiniteOptions< TData = InfiniteData, TQueryKey extends QueryKey = QueryKey, TPageParam = unknown, -> = OmitKeyof< +> = WithoutSkipTokenQueryFn< UseDeclarativeInfiniteQueryOptions< TQueryFnData, TError, TData, TQueryKey, TPageParam - >, - 'queryFn' -> & { - queryFn?: Exclude< - UseDeclarativeInfiniteQueryOptions< - TQueryFnData, - TError, - TData, - TQueryKey, - TPageParam - >['queryFn'], - SkipToken | undefined > -} +> export type UnusedSkipTokenImperativeInfiniteOptions< TQueryFnData, @@ -111,27 +119,15 @@ export type UnusedSkipTokenImperativeInfiniteOptions< TData = InfiniteData, TQueryKey extends QueryKey = QueryKey, TPageParam = unknown, -> = OmitKeyof< +> = WithoutSkipTokenQueryFn< UseImperativeInfiniteQueryOptions< TQueryFnData, TError, TData, TQueryKey, TPageParam - >, - 'queryFn' -> & { - queryFn?: Exclude< - UseImperativeInfiniteQueryOptions< - TQueryFnData, - TError, - TData, - TQueryKey, - TPageParam - >['queryFn'], - SkipToken | undefined > -} +> export type UnusedSkipTokenInfiniteOptions< TQueryFnData, @@ -189,12 +185,8 @@ export type DeclarativeDefinedInitialDataInfiniteOptions< TData, TQueryKey, TPageParam -> & { - initialData: - | NonUndefinedGuard> - | (() => NonUndefinedGuard>) - | undefined -} +> & + RequiredInitialData export type ImperativeDefinedInitialDataInfiniteOptions< TQueryFnData, @@ -208,12 +200,8 @@ export type ImperativeDefinedInitialDataInfiniteOptions< TData, TQueryKey, TPageParam -> & { - initialData: - | NonUndefinedGuard> - | (() => NonUndefinedGuard>) - | undefined -} +> & + RequiredInitialData export function infiniteQueryOptions< TQueryFnData, @@ -235,9 +223,8 @@ export function infiniteQueryOptions< TData, TQueryKey, TPageParam -> & { - queryKey: DataTag, TError> -} +> & + TaggedInfiniteQueryOptions<{}, TQueryKey, TQueryFnData, TError> export function infiniteQueryOptions< TQueryFnData, @@ -259,9 +246,8 @@ export function infiniteQueryOptions< TData, TQueryKey, TPageParam -> & { - queryKey: DataTag, TError> -} +> & + TaggedInfiniteQueryOptions<{}, TQueryKey, TQueryFnData, TError> export function infiniteQueryOptions< TQueryFnData, @@ -283,9 +269,8 @@ export function infiniteQueryOptions< TData, TQueryKey, TPageParam -> & { - queryKey: DataTag, TError> -} +> & + TaggedInfiniteQueryOptions<{}, TQueryKey, TQueryFnData, TError> export function infiniteQueryOptions< TQueryFnData, @@ -307,9 +292,8 @@ export function infiniteQueryOptions< TData, TQueryKey, TPageParam -> & { - queryKey: DataTag, TError> -} +> & + TaggedInfiniteQueryOptions<{}, TQueryKey, TQueryFnData, TError> export function infiniteQueryOptions< TQueryFnData, @@ -331,9 +315,8 @@ export function infiniteQueryOptions< TData, TQueryKey, TPageParam -> & { - queryKey: DataTag, TError> -} +> & + TaggedInfiniteQueryOptions<{}, TQueryKey, TQueryFnData, TError> export function infiniteQueryOptions< TQueryFnData, @@ -355,9 +338,8 @@ export function infiniteQueryOptions< TData, TQueryKey, TPageParam -> & { - queryKey: DataTag, TError> -} +> & + TaggedInfiniteQueryOptions<{}, TQueryKey, TQueryFnData, TError> export function infiniteQueryOptions< TQueryFnData, @@ -379,9 +361,8 @@ export function infiniteQueryOptions< TData, TQueryKey, TPageParam -> & { - queryKey: DataTag, TError> -} +> & + TaggedInfiniteQueryOptions<{}, TQueryKey, TQueryFnData, TError> export function infiniteQueryOptions< TQueryFnData, @@ -403,9 +384,8 @@ export function infiniteQueryOptions< TData, TQueryKey, TPageParam -> & { - queryKey: DataTag, TError> -} +> & + TaggedInfiniteQueryOptions<{}, TQueryKey, TQueryFnData, TError> export function infiniteQueryOptions(options: unknown) { return options From fc62de56b6a2a5ae628e4feeb0e9a0f8bb02ea2e Mon Sep 17 00:00:00 2001 From: TkDodo Date: Fri, 3 Apr 2026 17:17:54 +0200 Subject: [PATCH 12/25] fix core type error --- packages/query-core/src/infiniteQueryObserver.ts | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/packages/query-core/src/infiniteQueryObserver.ts b/packages/query-core/src/infiniteQueryObserver.ts index 16727e2e080..3d5f56ef2f9 100644 --- a/packages/query-core/src/infiniteQueryObserver.ts +++ b/packages/query-core/src/infiniteQueryObserver.ts @@ -162,7 +162,8 @@ export class InfiniteQueryObserver< fetchNextPage( ...args: Array ): Promise> { - const [{ pageParam, ...options } = {}] = args + const firstArg = args[0] ?? {} + const { pageParam, ...options } = firstArg return this.fetch({ ...options, meta: { @@ -177,7 +178,8 @@ export class InfiniteQueryObserver< fetchPreviousPage( ...args: Array ): Promise> { - const [{ pageParam, ...options } = {}] = args + const firstArg = args[0] ?? {} + const { pageParam, ...options } = firstArg return this.fetch({ ...options, meta: { From 6d0ebdb52bf67a561a3223fd0101a0a9c3d2dfe8 Mon Sep 17 00:00:00 2001 From: TkDodo Date: Fri, 3 Apr 2026 17:21:06 +0200 Subject: [PATCH 13/25] fix adapters --- packages/solid-query/src/QueryClient.ts | 11 +- .../src/__tests__/useQueryOptions.test-d.tsx | 20 ++++ .../solid-query/src/infiniteQueryOptions.ts | 79 +++++++++++- packages/solid-query/src/types.ts | 29 +++-- packages/solid-query/src/useInfiniteQuery.ts | 60 ++++++++-- .../svelte-query/src/createInfiniteQuery.ts | 47 +++++++- .../svelte-query/src/infiniteQueryOptions.ts | 39 +++++- packages/svelte-query/src/types.ts | 9 +- .../tests/infiniteQueryOptions.test-d.ts | 22 +++- .../__tests__/infiniteQueryOptions.test-d.ts | 20 ++++ .../vue-query/src/infiniteQueryOptions.ts | 112 +++++++++++++++++- packages/vue-query/src/queryClient.ts | 91 ++++++++++++-- packages/vue-query/src/useInfiniteQuery.ts | 72 +++++++++-- 13 files changed, 555 insertions(+), 56 deletions(-) diff --git a/packages/solid-query/src/QueryClient.ts b/packages/solid-query/src/QueryClient.ts index 998c92b808e..aee191ecc37 100644 --- a/packages/solid-query/src/QueryClient.ts +++ b/packages/solid-query/src/QueryClient.ts @@ -2,6 +2,7 @@ import { QueryClient as QueryCoreClient } from '@tanstack/query-core' import type { DefaultOptions as CoreDefaultOptions, DefaultError, + InfiniteQueryMode, OmitKeyof, QueryClientConfig as QueryCoreClientConfig, InfiniteQueryObserverOptions as QueryCoreInfiniteQueryObserverOptions, @@ -39,22 +40,24 @@ export interface QueryObserverOptions< | ((oldData: TData | undefined, newData: TData) => TData) } -export interface InfiniteQueryObserverOptions< +export type InfiniteQueryObserverOptions< TQueryFnData = unknown, TError = DefaultError, TData = TQueryFnData, TQueryKey extends QueryKey = QueryKey, TPageParam = unknown, -> extends OmitKeyof< + TMode extends InfiniteQueryMode | undefined = undefined, +> = OmitKeyof< QueryCoreInfiniteQueryObserverOptions< TQueryFnData, TError, TData, TQueryKey, - TPageParam + TPageParam, + TMode >, 'structuralSharing' -> { +> & { /** * Set this to a reconciliation key to enable reconciliation between query results. * Set this to `false` to disable reconciliation between query results. diff --git a/packages/solid-query/src/__tests__/useQueryOptions.test-d.tsx b/packages/solid-query/src/__tests__/useQueryOptions.test-d.tsx index d0f4d60446d..68e28eeadae 100644 --- a/packages/solid-query/src/__tests__/useQueryOptions.test-d.tsx +++ b/packages/solid-query/src/__tests__/useQueryOptions.test-d.tsx @@ -72,4 +72,24 @@ describe('infiniteQueryOptions', () => { }> >() }) + + it('should preserve imperative fetch method types', () => { + const options = infiniteQueryOptions({ + queryKey: ['key'], + queryFn: ({ pageParam }) => { + expectTypeOf(pageParam).toEqualTypeOf() + return pageParam * 5 + }, + initialPageParam: 1, + mode: 'imperative', + }) + + const query = useInfiniteQuery(() => options) + + query.fetchNextPage({ pageParam: 2 }) + query.fetchPreviousPage({ pageParam: 0 }) + + // @ts-expect-error pageParam is required in imperative mode + query.fetchNextPage() + }) }) diff --git a/packages/solid-query/src/infiniteQueryOptions.ts b/packages/solid-query/src/infiniteQueryOptions.ts index de4991d0b78..f188876a41b 100644 --- a/packages/solid-query/src/infiniteQueryOptions.ts +++ b/packages/solid-query/src/infiniteQueryOptions.ts @@ -2,6 +2,7 @@ import type { DataTag, DefaultError, InfiniteData, + InfiniteQueryMode, NonUndefinedGuard, QueryKey, } from '@tanstack/query-core' @@ -14,13 +15,15 @@ export type UndefinedInitialDataInfiniteOptions< TData = InfiniteData, TQueryKey extends QueryKey = QueryKey, TPageParam = unknown, + TMode extends InfiniteQueryMode | undefined = undefined, > = Accessor< SolidInfiniteQueryOptions< TQueryFnData, TError, TData, TQueryKey, - TPageParam + TPageParam, + TMode > & { initialData?: undefined } @@ -33,13 +36,15 @@ export type DefinedInitialDataInfiniteOptions< TData = InfiniteData, TQueryKey extends QueryKey = QueryKey, TPageParam = unknown, + TMode extends InfiniteQueryMode | undefined = undefined, > = Accessor< SolidInfiniteQueryOptions< TQueryFnData, TError, TData, TQueryKey, - TPageParam + TPageParam, + TMode > & { initialData: | NonUndefinedGuard> @@ -59,7 +64,8 @@ export function infiniteQueryOptions< TError, TData, TQueryKey, - TPageParam + TPageParam, + undefined > >, ): ReturnType< @@ -68,7 +74,66 @@ export function infiniteQueryOptions< TError, TData, TQueryKey, - TPageParam + TPageParam, + undefined + > +> & { + queryKey: DataTag> +} +export function infiniteQueryOptions< + TQueryFnData, + TError = DefaultError, + TData = InfiniteData, + TQueryKey extends QueryKey = QueryKey, + TPageParam = unknown, +>( + options: ReturnType< + DefinedInitialDataInfiniteOptions< + TQueryFnData, + TError, + TData, + TQueryKey, + TPageParam, + InfiniteQueryMode + > + >, +): ReturnType< + DefinedInitialDataInfiniteOptions< + TQueryFnData, + TError, + TData, + TQueryKey, + TPageParam, + InfiniteQueryMode + > +> & { + queryKey: DataTag> +} +export function infiniteQueryOptions< + TQueryFnData, + TError = DefaultError, + TData = InfiniteData, + TQueryKey extends QueryKey = QueryKey, + TPageParam = unknown, +>( + options: ReturnType< + UndefinedInitialDataInfiniteOptions< + TQueryFnData, + TError, + TData, + TQueryKey, + TPageParam, + undefined + > + >, +): ReturnType< + UndefinedInitialDataInfiniteOptions< + TQueryFnData, + TError, + TData, + TQueryKey, + TPageParam, + undefined > > & { queryKey: DataTag> @@ -86,7 +151,8 @@ export function infiniteQueryOptions< TError, TData, TQueryKey, - TPageParam + TPageParam, + InfiniteQueryMode > >, ): ReturnType< @@ -95,7 +161,8 @@ export function infiniteQueryOptions< TError, TData, TQueryKey, - TPageParam + TPageParam, + InfiniteQueryMode > > & { queryKey: DataTag> diff --git a/packages/solid-query/src/types.ts b/packages/solid-query/src/types.ts index 697177772cd..383b807a3a6 100644 --- a/packages/solid-query/src/types.ts +++ b/packages/solid-query/src/types.ts @@ -4,6 +4,7 @@ import type { DefaultError, DefinedInfiniteQueryObserverResult, DefinedQueryObserverResult, + InfiniteQueryMode, InfiniteQueryObserverResult, MutateFunction, MutationObserverOptions, @@ -87,22 +88,24 @@ export type DefinedUseQueryResult< > = DefinedUseBaseQueryResult /* --- Create Infinite Queries Types --- */ -export interface SolidInfiniteQueryOptions< +export type SolidInfiniteQueryOptions< TQueryFnData = unknown, TError = DefaultError, TData = TQueryFnData, TQueryKey extends QueryKey = QueryKey, TPageParam = unknown, -> extends OmitKeyof< + TMode extends InfiniteQueryMode | undefined = undefined, +> = OmitKeyof< InfiniteQueryObserverOptions< TQueryFnData, TError, TData, TQueryKey, - TPageParam + TPageParam, + TMode >, 'queryKey' | 'suspense' -> { +> & { queryKey: TQueryKey /** * Only applicable while rendering queries on the server with streaming. @@ -125,19 +128,31 @@ export type UseInfiniteQueryOptions< TData = TQueryFnData, TQueryKey extends QueryKey = QueryKey, TPageParam = unknown, + TMode extends InfiniteQueryMode | undefined = undefined, > = Accessor< - SolidInfiniteQueryOptions + SolidInfiniteQueryOptions< + TQueryFnData, + TError, + TData, + TQueryKey, + TPageParam, + TMode + > > export type UseInfiniteQueryResult< TData = unknown, TError = DefaultError, -> = InfiniteQueryObserverResult + TPageParam = unknown, + TMode extends InfiniteQueryMode | undefined = undefined, +> = InfiniteQueryObserverResult export type DefinedUseInfiniteQueryResult< TData = unknown, TError = DefaultError, -> = DefinedInfiniteQueryObserverResult + TPageParam = unknown, + TMode extends InfiniteQueryMode | undefined = undefined, +> = DefinedInfiniteQueryObserverResult /* --- Create Mutation Types --- */ export interface SolidMutationOptions< diff --git a/packages/solid-query/src/useInfiniteQuery.ts b/packages/solid-query/src/useInfiniteQuery.ts index 9dcff4b1855..7861810ed29 100644 --- a/packages/solid-query/src/useInfiniteQuery.ts +++ b/packages/solid-query/src/useInfiniteQuery.ts @@ -4,6 +4,7 @@ import { useBaseQuery } from './useBaseQuery' import type { DefaultError, InfiniteData, + InfiniteQueryMode, QueryKey, QueryObserver, } from '@tanstack/query-core' @@ -31,10 +32,33 @@ export function useInfiniteQuery< TError, TData, TQueryKey, - TPageParam + TPageParam, + undefined >, queryClient?: Accessor, -): DefinedUseInfiniteQueryResult +): DefinedUseInfiniteQueryResult +export function useInfiniteQuery< + TQueryFnData, + TError = DefaultError, + TData = InfiniteData, + TQueryKey extends QueryKey = QueryKey, + TPageParam = unknown, +>( + options: DefinedInitialDataInfiniteOptions< + TQueryFnData, + TError, + TData, + TQueryKey, + TPageParam, + InfiniteQueryMode + >, + queryClient?: Accessor, +): DefinedUseInfiniteQueryResult< + TData, + TError, + TPageParam, + InfiniteQueryMode +> export function useInfiniteQuery< TQueryFnData, TError = DefaultError, @@ -47,11 +71,28 @@ export function useInfiniteQuery< TError, TData, TQueryKey, - TPageParam + TPageParam, + undefined >, queryClient?: Accessor, -): UseInfiniteQueryResult - +): UseInfiniteQueryResult +export function useInfiniteQuery< + TQueryFnData, + TError = DefaultError, + TData = InfiniteData, + TQueryKey extends QueryKey = QueryKey, + TPageParam = unknown, +>( + options: UndefinedInitialDataInfiniteOptions< + TQueryFnData, + TError, + TData, + TQueryKey, + TPageParam, + InfiniteQueryMode + >, + queryClient?: Accessor, +): UseInfiniteQueryResult export function useInfiniteQuery< TQueryFnData, TError = DefaultError, @@ -67,10 +108,15 @@ export function useInfiniteQuery< TPageParam >, queryClient?: Accessor, -): UseInfiniteQueryResult { +): UseInfiniteQueryResult + +export function useInfiniteQuery( + options: any, + queryClient?: Accessor, +): any { return useBaseQuery( createMemo(() => options()), InfiniteQueryObserver as typeof QueryObserver, queryClient, - ) as UseInfiniteQueryResult + ) as UseInfiniteQueryResult } diff --git a/packages/svelte-query/src/createInfiniteQuery.ts b/packages/svelte-query/src/createInfiniteQuery.ts index e8fe7948909..b81939ceea3 100644 --- a/packages/svelte-query/src/createInfiniteQuery.ts +++ b/packages/svelte-query/src/createInfiniteQuery.ts @@ -3,12 +3,14 @@ import { createBaseQuery } from './createBaseQuery.svelte.js' import type { DefaultError, InfiniteData, + InfiniteQueryMode, QueryClient, QueryKey, QueryObserver, } from '@tanstack/query-core' import type { Accessor, + CreateBaseQueryOptions, CreateInfiniteQueryOptions, CreateInfiniteQueryResult, } from './types.js' @@ -26,14 +28,51 @@ export function createInfiniteQuery< TError, TData, TQueryKey, - TPageParam + TPageParam, + undefined > >, queryClient?: Accessor, -): CreateInfiniteQueryResult { +): CreateInfiniteQueryResult +export function createInfiniteQuery< + TQueryFnData, + TError = DefaultError, + TData = InfiniteData, + TQueryKey extends QueryKey = QueryKey, + TPageParam = unknown, +>( + options: Accessor< + CreateInfiniteQueryOptions< + TQueryFnData, + TError, + TData, + TQueryKey, + TPageParam, + InfiniteQueryMode + > + >, + queryClient?: Accessor, +): CreateInfiniteQueryResult< + TData, + TError, + TPageParam, + InfiniteQueryMode +> +export function createInfiniteQuery( + options: any, + queryClient?: Accessor, +): any { return createBaseQuery( - options, + options as Accessor< + CreateBaseQueryOptions< + any, + any, + any, + InfiniteData, + any + > + >, InfiniteQueryObserver as typeof QueryObserver, queryClient, - ) as CreateInfiniteQueryResult + ) as any } diff --git a/packages/svelte-query/src/infiniteQueryOptions.ts b/packages/svelte-query/src/infiniteQueryOptions.ts index 9702520ac22..c0255632e89 100644 --- a/packages/svelte-query/src/infiniteQueryOptions.ts +++ b/packages/svelte-query/src/infiniteQueryOptions.ts @@ -1,4 +1,9 @@ -import type { DefaultError, InfiniteData, QueryKey } from '@tanstack/query-core' +import type { + DefaultError, + InfiniteData, + InfiniteQueryMode, + QueryKey, +} from '@tanstack/query-core' import type { CreateInfiniteQueryOptions } from './types.js' export function infiniteQueryOptions< @@ -13,14 +18,40 @@ export function infiniteQueryOptions< TError, TData, TQueryKey, - TPageParam + TPageParam, + undefined >, ): CreateInfiniteQueryOptions< TQueryFnData, TError, TData, TQueryKey, - TPageParam -> { + TPageParam, + undefined +> +export function infiniteQueryOptions< + TQueryFnData, + TError = DefaultError, + TData = InfiniteData, + TQueryKey extends QueryKey = QueryKey, + TPageParam = unknown, +>( + options: CreateInfiniteQueryOptions< + TQueryFnData, + TError, + TData, + TQueryKey, + TPageParam, + InfiniteQueryMode + >, +): CreateInfiniteQueryOptions< + TQueryFnData, + TError, + TData, + TQueryKey, + TPageParam, + InfiniteQueryMode +> +export function infiniteQueryOptions(options: any): any { return options } diff --git a/packages/svelte-query/src/types.ts b/packages/svelte-query/src/types.ts index 9a64d10dc3f..e79f56001ae 100644 --- a/packages/svelte-query/src/types.ts +++ b/packages/svelte-query/src/types.ts @@ -2,6 +2,7 @@ import type { Snippet } from 'svelte' import type { DefaultError, DefinedQueryObserverResult, + InfiniteQueryMode, InfiniteQueryObserverOptions, InfiniteQueryObserverResult, MutateFunction, @@ -56,19 +57,23 @@ export type CreateInfiniteQueryOptions< TData = TQueryFnData, TQueryKey extends QueryKey = QueryKey, TPageParam = unknown, + TMode extends InfiniteQueryMode | undefined = undefined, > = InfiniteQueryObserverOptions< TQueryFnData, TError, TData, TQueryKey, - TPageParam + TPageParam, + TMode > /** Result from createInfiniteQuery */ export type CreateInfiniteQueryResult< TData = unknown, TError = DefaultError, -> = InfiniteQueryObserverResult + TPageParam = unknown, + TMode extends InfiniteQueryMode | undefined = undefined, +> = InfiniteQueryObserverResult /** Options for createBaseQuery with initialData */ export type DefinedCreateBaseQueryResult< diff --git a/packages/svelte-query/tests/infiniteQueryOptions.test-d.ts b/packages/svelte-query/tests/infiniteQueryOptions.test-d.ts index b92d8d7730e..a2cdbf0daae 100644 --- a/packages/svelte-query/tests/infiniteQueryOptions.test-d.ts +++ b/packages/svelte-query/tests/infiniteQueryOptions.test-d.ts @@ -5,12 +5,12 @@ import type { InfiniteData } from '@tanstack/query-core' describe('queryOptions', () => { test('Should not allow excess properties', () => { + // @ts-expect-error this is a good error, because stallTime does not exist! infiniteQueryOptions({ queryKey: ['key'], queryFn: () => Promise.resolve('data'), getNextPageParam: () => 1, initialPageParam: 1, - // @ts-expect-error this is a good error, because stallTime does not exist! stallTime: 1000, }) }) @@ -56,4 +56,24 @@ describe('queryOptions', () => { expectTypeOf(data).toEqualTypeOf>() }) + + test('Should preserve imperative fetch method types', () => { + const options = infiniteQueryOptions({ + queryKey: ['key'], + queryFn: ({ pageParam }) => { + expectTypeOf(pageParam).toEqualTypeOf() + return pageParam * 5 + }, + initialPageParam: 1, + mode: 'imperative', + }) + + const query = createInfiniteQuery(() => options) + + query.fetchNextPage({ pageParam: 2 }) + query.fetchPreviousPage({ pageParam: 0 }) + + // @ts-expect-error pageParam is required in imperative mode + query.fetchNextPage() + }) }) diff --git a/packages/vue-query/src/__tests__/infiniteQueryOptions.test-d.ts b/packages/vue-query/src/__tests__/infiniteQueryOptions.test-d.ts index 6413126ffd7..df3ac49765d 100644 --- a/packages/vue-query/src/__tests__/infiniteQueryOptions.test-d.ts +++ b/packages/vue-query/src/__tests__/infiniteQueryOptions.test-d.ts @@ -110,4 +110,24 @@ describe('infiniteQueryOptions', () => { InfiniteData | undefined >() }) + + it('should preserve imperative fetch method types', () => { + const options = infiniteQueryOptions({ + queryKey: ['key'], + queryFn: ({ pageParam }) => { + expectTypeOf(pageParam).toEqualTypeOf() + return pageParam * 5 + }, + initialPageParam: 1, + mode: 'imperative', + }) + + const query = reactive(useInfiniteQuery(options)) + + query.fetchNextPage({ pageParam: 2 }) + query.fetchPreviousPage({ pageParam: 0 }) + + // @ts-expect-error pageParam is required in imperative mode + query.fetchNextPage() + }) }) diff --git a/packages/vue-query/src/infiniteQueryOptions.ts b/packages/vue-query/src/infiniteQueryOptions.ts index adab774aa10..5b210c1c951 100644 --- a/packages/vue-query/src/infiniteQueryOptions.ts +++ b/packages/vue-query/src/infiniteQueryOptions.ts @@ -2,6 +2,7 @@ import type { DataTag, DefaultError, InfiniteData, + InfiniteQueryMode, NonUndefinedGuard, QueryKey, } from '@tanstack/query-core' @@ -13,12 +14,14 @@ export type UndefinedInitialDataInfiniteOptions< TData = InfiniteData, TQueryKey extends QueryKey = QueryKey, TPageParam = unknown, + TMode extends InfiniteQueryMode | undefined = undefined, > = UseInfiniteQueryOptions< TQueryFnData, TError, TData, TQueryKey, - TPageParam + TPageParam, + TMode > & { initialData?: undefined } @@ -29,18 +32,123 @@ export type DefinedInitialDataInfiniteOptions< TData = InfiniteData, TQueryKey extends QueryKey = QueryKey, TPageParam = unknown, + TMode extends InfiniteQueryMode | undefined = undefined, > = UseInfiniteQueryOptions< TQueryFnData, TError, TData, TQueryKey, - TPageParam + TPageParam, + TMode > & { initialData: | NonUndefinedGuard> | (() => NonUndefinedGuard>) } +export function infiniteQueryOptions< + TQueryFnData, + TError = DefaultError, + TData = InfiniteData, + TQueryKey extends QueryKey = QueryKey, + TPageParam = unknown, +>( + options: UndefinedInitialDataInfiniteOptions< + TQueryFnData, + TError, + TData, + TQueryKey, + TPageParam, + undefined + >, +): UndefinedInitialDataInfiniteOptions< + TQueryFnData, + TError, + TData, + TQueryKey, + TPageParam, + undefined +> & { + queryKey: DataTag, TError> +} +export function infiniteQueryOptions< + TQueryFnData, + TError = DefaultError, + TData = InfiniteData, + TQueryKey extends QueryKey = QueryKey, + TPageParam = unknown, +>( + options: UndefinedInitialDataInfiniteOptions< + TQueryFnData, + TError, + TData, + TQueryKey, + TPageParam, + InfiniteQueryMode + >, +): UndefinedInitialDataInfiniteOptions< + TQueryFnData, + TError, + TData, + TQueryKey, + TPageParam, + InfiniteQueryMode +> & { + queryKey: DataTag, TError> +} + +export function infiniteQueryOptions< + TQueryFnData, + TError = DefaultError, + TData = InfiniteData, + TQueryKey extends QueryKey = QueryKey, + TPageParam = unknown, +>( + options: DefinedInitialDataInfiniteOptions< + TQueryFnData, + TError, + TData, + TQueryKey, + TPageParam, + undefined + >, +): DefinedInitialDataInfiniteOptions< + TQueryFnData, + TError, + TData, + TQueryKey, + TPageParam, + undefined +> & { + queryKey: DataTag, TError> +} + +export function infiniteQueryOptions< + TQueryFnData, + TError = DefaultError, + TData = InfiniteData, + TQueryKey extends QueryKey = QueryKey, + TPageParam = unknown, +>( + options: DefinedInitialDataInfiniteOptions< + TQueryFnData, + TError, + TData, + TQueryKey, + TPageParam, + InfiniteQueryMode + >, +): DefinedInitialDataInfiniteOptions< + TQueryFnData, + TError, + TData, + TQueryKey, + TPageParam, + InfiniteQueryMode +> & { + queryKey: DataTag, TError> +} + export function infiniteQueryOptions< TQueryFnData, TError = DefaultError, diff --git a/packages/vue-query/src/queryClient.ts b/packages/vue-query/src/queryClient.ts index 7f9a0894bfe..3ef5ab96233 100644 --- a/packages/vue-query/src/queryClient.ts +++ b/packages/vue-query/src/queryClient.ts @@ -16,6 +16,7 @@ import type { InferDataFromTag, InferErrorFromTag, InfiniteData, + InfiniteQueryMode, InvalidateOptions, InvalidateQueryFilters, MutationFilters, @@ -336,7 +337,42 @@ export class QueryClient extends QC { TError, TData, TQueryKey, - TPageParam + TPageParam, + undefined + >, + ): Promise> + fetchInfiniteQuery< + TQueryFnData = unknown, + TError = DefaultError, + TData = TQueryFnData, + TQueryKey extends QueryKey = QueryKey, + TPageParam = unknown, + >( + options: FetchInfiniteQueryOptions< + TQueryFnData, + TError, + TData, + TQueryKey, + TPageParam, + InfiniteQueryMode + >, + ): Promise> + fetchInfiniteQuery< + TQueryFnData, + TError = DefaultError, + TData = TQueryFnData, + TQueryKey extends QueryKey = QueryKey, + TPageParam = unknown, + >( + options: MaybeRefDeep< + FetchInfiniteQueryOptions< + TQueryFnData, + TError, + TData, + TQueryKey, + TPageParam, + undefined + > >, ): Promise> fetchInfiniteQuery< @@ -352,7 +388,8 @@ export class QueryClient extends QC { TError, TData, TQueryKey, - TPageParam + TPageParam, + InfiniteQueryMode > >, ): Promise> @@ -369,11 +406,12 @@ export class QueryClient extends QC { TError, TData, TQueryKey, - TPageParam + TPageParam, + InfiniteQueryMode | undefined > >, ): Promise> { - return super.fetchInfiniteQuery(cloneDeepUnref(options)) + return super.fetchInfiniteQuery(cloneDeepUnref(options) as any) } prefetchInfiniteQuery< @@ -388,7 +426,42 @@ export class QueryClient extends QC { TError, TData, TQueryKey, - TPageParam + TPageParam, + undefined + >, + ): Promise + prefetchInfiniteQuery< + TQueryFnData, + TError = DefaultError, + TData = TQueryFnData, + TQueryKey extends QueryKey = QueryKey, + TPageParam = unknown, + >( + options: FetchInfiniteQueryOptions< + TQueryFnData, + TError, + TData, + TQueryKey, + TPageParam, + InfiniteQueryMode + >, + ): Promise + prefetchInfiniteQuery< + TQueryFnData, + TError = DefaultError, + TData = TQueryFnData, + TQueryKey extends QueryKey = QueryKey, + TPageParam = unknown, + >( + options: MaybeRefDeep< + FetchInfiniteQueryOptions< + TQueryFnData, + TError, + TData, + TQueryKey, + TPageParam, + undefined + > >, ): Promise prefetchInfiniteQuery< @@ -404,7 +477,8 @@ export class QueryClient extends QC { TError, TData, TQueryKey, - TPageParam + TPageParam, + InfiniteQueryMode > >, ): Promise @@ -421,11 +495,12 @@ export class QueryClient extends QC { TError, TData, TQueryKey, - TPageParam + TPageParam, + InfiniteQueryMode | undefined > >, ): Promise { - return super.prefetchInfiniteQuery(cloneDeepUnref(options)) + return super.prefetchInfiniteQuery(cloneDeepUnref(options) as any) } setDefaultOptions(options: MaybeRefDeep): void { diff --git a/packages/vue-query/src/useInfiniteQuery.ts b/packages/vue-query/src/useInfiniteQuery.ts index 107251df872..8b12fb7fb5b 100644 --- a/packages/vue-query/src/useInfiniteQuery.ts +++ b/packages/vue-query/src/useInfiniteQuery.ts @@ -7,6 +7,7 @@ import type { import type { DefaultError, InfiniteData, + InfiniteQueryMode, InfiniteQueryObserverOptions, InfiniteQueryObserverResult, QueryKey, @@ -30,6 +31,7 @@ export type UseInfiniteQueryOptions< TData = TQueryFnData, TQueryKey extends QueryKey = QueryKey, TPageParam = unknown, + TMode extends InfiniteQueryMode | undefined = undefined, > = MaybeRef< { [Property in keyof InfiniteQueryObserverOptions< @@ -37,7 +39,8 @@ export type UseInfiniteQueryOptions< TError, TData, TQueryKey, - TPageParam + TPageParam, + TMode >]: Property extends 'enabled' ? MaybeRefOrGetter< InfiniteQueryObserverOptions< @@ -45,7 +48,8 @@ export type UseInfiniteQueryOptions< TError, TData, DeepUnwrapRef, - TPageParam + TPageParam, + TMode >[Property] > : MaybeRefDeep< @@ -54,16 +58,22 @@ export type UseInfiniteQueryOptions< TError, TData, DeepUnwrapRef, - TPageParam + TPageParam, + TMode >[Property] > } & ShallowOption > -export type UseInfiniteQueryReturnType = UseBaseQueryReturnType< +export type UseInfiniteQueryReturnType< TData, TError, - InfiniteQueryObserverResult + TPageParam = unknown, + TMode extends InfiniteQueryMode | undefined = undefined, +> = UseBaseQueryReturnType< + TData, + TError, + InfiniteQueryObserverResult > export function useInfiniteQuery< @@ -79,11 +89,31 @@ export function useInfiniteQuery< TError, TData, TQueryKey, - TPageParam + TPageParam, + undefined > >, queryClient?: QueryClient, -): UseInfiniteQueryReturnType +): UseInfiniteQueryReturnType +export function useInfiniteQuery< + TQueryFnData, + TError = DefaultError, + TData = InfiniteData, + TQueryKey extends QueryKey = QueryKey, + TPageParam = unknown, +>( + options: MaybeRefOrGetter< + DefinedInitialDataInfiniteOptions< + TQueryFnData, + TError, + TData, + TQueryKey, + TPageParam, + InfiniteQueryMode + > + >, + queryClient?: QueryClient, +): UseInfiniteQueryReturnType export function useInfiniteQuery< TQueryFnData, @@ -98,11 +128,31 @@ export function useInfiniteQuery< TError, TData, TQueryKey, - TPageParam + TPageParam, + undefined > >, queryClient?: QueryClient, -): UseInfiniteQueryReturnType +): UseInfiniteQueryReturnType +export function useInfiniteQuery< + TQueryFnData, + TError = DefaultError, + TData = InfiniteData, + TQueryKey extends QueryKey = QueryKey, + TPageParam = unknown, +>( + options: MaybeRefOrGetter< + UndefinedInitialDataInfiniteOptions< + TQueryFnData, + TError, + TData, + TQueryKey, + TPageParam, + InfiniteQueryMode + > + >, + queryClient?: QueryClient, +): UseInfiniteQueryReturnType export function useInfiniteQuery< TQueryFnData, @@ -118,9 +168,9 @@ export function useInfiniteQuery< ): UseInfiniteQueryReturnType export function useInfiniteQuery( - options: MaybeRefOrGetter, + options: any, queryClient?: QueryClient, -) { +): any { return useBaseQuery( InfiniteQueryObserver as typeof QueryObserver, options, From 4947e2bd45b10a0338ac4c46b4c8ac961e65ee2b Mon Sep 17 00:00:00 2001 From: TkDodo Date: Fri, 3 Apr 2026 19:49:07 +0200 Subject: [PATCH 14/25] angular adapter --- .../infinite-query-options.test-d.ts | 31 +- .../__tests__/inject-infinite-query.test-d.ts | 10 +- .../src/infinite-query-options.ts | 314 ++++++++++++++---- .../src/inject-infinite-query.ts | 115 +++++-- .../angular-query-experimental/src/types.ts | 55 ++- 5 files changed, 418 insertions(+), 107 deletions(-) diff --git a/packages/angular-query-experimental/src/__tests__/infinite-query-options.test-d.ts b/packages/angular-query-experimental/src/__tests__/infinite-query-options.test-d.ts index 0d04fc8ce6a..3c40189cf39 100644 --- a/packages/angular-query-experimental/src/__tests__/infinite-query-options.test-d.ts +++ b/packages/angular-query-experimental/src/__tests__/infinite-query-options.test-d.ts @@ -48,9 +48,30 @@ describe('infiniteQueryOptions', () => { expectTypeOf(data()).toEqualTypeOf< InfiniteData | undefined >() - expectTypeOf(fetchNextPage).parameters.toEqualTypeOf< - [options: { pageParam: number; cancelRefetch?: boolean; throwOnError?: boolean }] - >() + fetchNextPage({ pageParam: 2 }) + + // @ts-expect-error pageParam is required in imperative mode + fetchNextPage() + }) + + it('should preserve imperative fetch method types', () => { + const options = infiniteQueryOptions({ + queryKey: ['key'], + queryFn: ({ pageParam }) => { + expectTypeOf(pageParam).toEqualTypeOf() + return pageParam * 5 + }, + initialPageParam: 1, + mode: 'imperative', + }) + + const { fetchNextPage, fetchPreviousPage } = injectInfiniteQuery(() => options) + + fetchNextPage({ pageParam: 2 }) + fetchPreviousPage({ pageParam: 0 }) + + // @ts-expect-error pageParam is required in imperative mode + fetchNextPage() }) it('should work when passed to fetchInfiniteQuery', async () => { @@ -166,21 +187,21 @@ describe('infiniteQueryOptions', () => { initialPageParam: 1, }) + // @ts-expect-error getNextPageParam is not allowed in imperative mode infiniteQueryOptions({ queryKey: ['key'], queryFn: () => Promise.resolve('string'), initialPageParam: 1, mode: 'imperative', - // @ts-expect-error getNextPageParam is not allowed in imperative mode getNextPageParam: () => 1, }) + // @ts-expect-error getPreviousPageParam is not allowed in imperative mode infiniteQueryOptions({ queryKey: ['key'], queryFn: () => Promise.resolve('string'), initialPageParam: 1, mode: 'imperative', - // @ts-expect-error getPreviousPageParam is not allowed in imperative mode getPreviousPageParam: () => 0, }) }) diff --git a/packages/angular-query-experimental/src/__tests__/inject-infinite-query.test-d.ts b/packages/angular-query-experimental/src/__tests__/inject-infinite-query.test-d.ts index 7808154411a..388511aeefc 100644 --- a/packages/angular-query-experimental/src/__tests__/inject-infinite-query.test-d.ts +++ b/packages/angular-query-experimental/src/__tests__/inject-infinite-query.test-d.ts @@ -50,12 +50,10 @@ describe('injectInfiniteQuery', () => { })) }) - expectTypeOf(query.fetchNextPage).parameters.toEqualTypeOf< - [options: { pageParam: number; cancelRefetch?: boolean; throwOnError?: boolean }] - >() + query.fetchNextPage({ pageParam: 1 }) + query.fetchPreviousPage({ pageParam: 0 }) - expectTypeOf(query.fetchPreviousPage).parameters.toEqualTypeOf< - [options: { pageParam: number; cancelRefetch?: boolean; throwOnError?: boolean }] - >() + // @ts-expect-error pageParam is required in imperative mode + query.fetchNextPage() }) }) diff --git a/packages/angular-query-experimental/src/infinite-query-options.ts b/packages/angular-query-experimental/src/infinite-query-options.ts index fc18c0e94dc..fd412dc0f5b 100644 --- a/packages/angular-query-experimental/src/infinite-query-options.ts +++ b/packages/angular-query-experimental/src/infinite-query-options.ts @@ -3,12 +3,40 @@ import type { DefaultError, InfiniteData, InitialDataFunction, + InfiniteQueryMode, NonUndefinedGuard, OmitKeyof, QueryKey, SkipToken, } from '@tanstack/query-core' -import type { CreateInfiniteQueryOptions } from './types' +import type { + CreateDeclarativeInfiniteQueryOptions, + CreateImperativeInfiniteQueryOptions, + CreateInfiniteQueryOptions, +} from './types' + +type OptionalInitialData = { + initialData?: + | undefined + | NonUndefinedGuard> + | InitialDataFunction< + NonUndefinedGuard> + > +} + +type RequiredInitialData = { + initialData: + | NonUndefinedGuard> + | (() => NonUndefinedGuard>) + | undefined +} + +type WithoutSkipTokenQueryFn = OmitKeyof< + TOptions, + 'queryFn' +> & { + queryFn?: Exclude +} export type UndefinedInitialDataInfiniteOptions< TQueryFnData, @@ -16,48 +44,130 @@ export type UndefinedInitialDataInfiniteOptions< TData = InfiniteData, TQueryKey extends QueryKey = QueryKey, TPageParam = unknown, + TMode extends InfiniteQueryMode | undefined = InfiniteQueryMode | undefined, > = CreateInfiniteQueryOptions< + TQueryFnData, + TError, + TData, + TQueryKey, + TPageParam, + TMode +> & + OptionalInitialData + +export type DeclarativeUndefinedInitialDataInfiniteOptions< + TQueryFnData, + TError = DefaultError, + TData = InfiniteData, + TQueryKey extends QueryKey = QueryKey, + TPageParam = unknown, +> = CreateDeclarativeInfiniteQueryOptions< TQueryFnData, TError, TData, TQueryKey, TPageParam -> & { - initialData?: - | undefined - | NonUndefinedGuard> - | InitialDataFunction< - NonUndefinedGuard> - > -} +> & + OptionalInitialData -export type UnusedSkipTokenInfiniteOptions< +export type ImperativeUndefinedInitialDataInfiniteOptions< TQueryFnData, TError = DefaultError, TData = InfiniteData, TQueryKey extends QueryKey = QueryKey, TPageParam = unknown, -> = OmitKeyof< - CreateInfiniteQueryOptions< +> = CreateImperativeInfiniteQueryOptions< + TQueryFnData, + TError, + TData, + TQueryKey, + TPageParam +> & + OptionalInitialData + +export type UnusedSkipTokenDeclarativeInfiniteOptions< + TQueryFnData, + TError = DefaultError, + TData = InfiniteData, + TQueryKey extends QueryKey = QueryKey, + TPageParam = unknown, +> = WithoutSkipTokenQueryFn< + CreateDeclarativeInfiniteQueryOptions< TQueryFnData, TError, TData, TQueryKey, TPageParam - >, - 'queryFn' -> & { - queryFn?: Exclude< - CreateInfiniteQueryOptions< + > +> + +export type UnusedSkipTokenImperativeInfiniteOptions< + TQueryFnData, + TError = DefaultError, + TData = InfiniteData, + TQueryKey extends QueryKey = QueryKey, + TPageParam = unknown, +> = WithoutSkipTokenQueryFn< + CreateImperativeInfiniteQueryOptions< + TQueryFnData, + TError, + TData, + TQueryKey, + TPageParam + > +> + +export type UnusedSkipTokenInfiniteOptions< + TQueryFnData, + TError = DefaultError, + TData = InfiniteData, + TQueryKey extends QueryKey = QueryKey, + TPageParam = unknown, +> = + | UnusedSkipTokenDeclarativeInfiniteOptions< TQueryFnData, TError, TData, TQueryKey, TPageParam - >['queryFn'], - SkipToken | undefined - > -} + > + | UnusedSkipTokenImperativeInfiniteOptions< + TQueryFnData, + TError, + TData, + TQueryKey, + TPageParam + > + +export type DeclarativeDefinedInitialDataInfiniteOptions< + TQueryFnData, + TError = DefaultError, + TData = InfiniteData, + TQueryKey extends QueryKey = QueryKey, + TPageParam = unknown, +> = CreateDeclarativeInfiniteQueryOptions< + TQueryFnData, + TError, + TData, + TQueryKey, + TPageParam +> & + RequiredInitialData + +export type ImperativeDefinedInitialDataInfiniteOptions< + TQueryFnData, + TError = DefaultError, + TData = InfiniteData, + TQueryKey extends QueryKey = QueryKey, + TPageParam = unknown, +> = CreateImperativeInfiniteQueryOptions< + TQueryFnData, + TError, + TData, + TQueryKey, + TPageParam +> & + RequiredInitialData export type DefinedInitialDataInfiniteOptions< TQueryFnData, @@ -65,26 +175,40 @@ export type DefinedInitialDataInfiniteOptions< TData = InfiniteData, TQueryKey extends QueryKey = QueryKey, TPageParam = unknown, + TMode extends InfiniteQueryMode | undefined = InfiniteQueryMode | undefined, > = CreateInfiniteQueryOptions< + TQueryFnData, + TError, + TData, + TQueryKey, + TPageParam, + TMode +> & + RequiredInitialData + +export function infiniteQueryOptions< + TQueryFnData, + TError = DefaultError, + TData = InfiniteData, + TQueryKey extends QueryKey = QueryKey, + TPageParam = unknown, +>( + options: DeclarativeDefinedInitialDataInfiniteOptions< + TQueryFnData, + TError, + TData, + TQueryKey, + TPageParam + >, +): DeclarativeDefinedInitialDataInfiniteOptions< TQueryFnData, TError, TData, TQueryKey, TPageParam > & { - initialData: - | NonUndefinedGuard> - | (() => NonUndefinedGuard>) - | undefined + queryKey: DataTag, TError> } - -/** - * Allows to share and re-use infinite query options in a type-safe way. - * - * The `queryKey` will be tagged with the type from `queryFn`. - * @param options - The infinite query options to tag with the type from `queryFn`. - * @returns The tagged infinite query options. - */ export function infiniteQueryOptions< TQueryFnData, TError = DefaultError, @@ -92,14 +216,14 @@ export function infiniteQueryOptions< TQueryKey extends QueryKey = QueryKey, TPageParam = unknown, >( - options: DefinedInitialDataInfiniteOptions< + options: ImperativeDefinedInitialDataInfiniteOptions< TQueryFnData, TError, TData, TQueryKey, TPageParam >, -): DefinedInitialDataInfiniteOptions< +): ImperativeDefinedInitialDataInfiniteOptions< TQueryFnData, TError, TData, @@ -108,14 +232,6 @@ export function infiniteQueryOptions< > & { queryKey: DataTag, TError> } - -/** - * Allows to share and re-use infinite query options in a type-safe way. - * - * The `queryKey` will be tagged with the type from `queryFn`. - * @param options - The infinite query options to tag with the type from `queryFn`. - * @returns The tagged infinite query options. - */ export function infiniteQueryOptions< TQueryFnData, TError = DefaultError, @@ -123,14 +239,106 @@ export function infiniteQueryOptions< TQueryKey extends QueryKey = QueryKey, TPageParam = unknown, >( - options: UnusedSkipTokenInfiniteOptions< + options: UnusedSkipTokenDeclarativeInfiniteOptions< TQueryFnData, TError, TData, TQueryKey, TPageParam >, -): UnusedSkipTokenInfiniteOptions< +): UnusedSkipTokenDeclarativeInfiniteOptions< + TQueryFnData, + TError, + TData, + TQueryKey, + TPageParam +> & { + queryKey: DataTag, TError> +} +export function infiniteQueryOptions< + TQueryFnData, + TError = DefaultError, + TData = InfiniteData, + TQueryKey extends QueryKey = QueryKey, + TPageParam = unknown, +>( + options: UnusedSkipTokenImperativeInfiniteOptions< + TQueryFnData, + TError, + TData, + TQueryKey, + TPageParam + >, +): UnusedSkipTokenImperativeInfiniteOptions< + TQueryFnData, + TError, + TData, + TQueryKey, + TPageParam +> & { + queryKey: DataTag, TError> +} +export function infiniteQueryOptions< + TQueryFnData, + TError = DefaultError, + TData = InfiniteData, + TQueryKey extends QueryKey = QueryKey, + TPageParam = unknown, +>( + options: DeclarativeUndefinedInitialDataInfiniteOptions< + TQueryFnData, + TError, + TData, + TQueryKey, + TPageParam + >, +): DeclarativeUndefinedInitialDataInfiniteOptions< + TQueryFnData, + TError, + TData, + TQueryKey, + TPageParam +> & { + queryKey: DataTag, TError> +} +export function infiniteQueryOptions< + TQueryFnData, + TError = DefaultError, + TData = InfiniteData, + TQueryKey extends QueryKey = QueryKey, + TPageParam = unknown, +>( + options: ImperativeUndefinedInitialDataInfiniteOptions< + TQueryFnData, + TError, + TData, + TQueryKey, + TPageParam + >, +): ImperativeUndefinedInitialDataInfiniteOptions< + TQueryFnData, + TError, + TData, + TQueryKey, + TPageParam +> & { + queryKey: DataTag, TError> +} +export function infiniteQueryOptions< + TQueryFnData, + TError = DefaultError, + TData = InfiniteData, + TQueryKey extends QueryKey = QueryKey, + TPageParam = unknown, +>( + options: DefinedInitialDataInfiniteOptions< + TQueryFnData, + TError, + TData, + TQueryKey, + TPageParam + >, +): DefinedInitialDataInfiniteOptions< TQueryFnData, TError, TData, @@ -139,14 +347,6 @@ export function infiniteQueryOptions< > & { queryKey: DataTag, TError> } - -/** - * Allows to share and re-use infinite query options in a type-safe way. - * - * The `queryKey` will be tagged with the type from `queryFn`. - * @param options - The infinite query options to tag with the type from `queryFn`. - * @returns The tagged infinite query options. - */ export function infiniteQueryOptions< TQueryFnData, TError = DefaultError, @@ -170,14 +370,6 @@ export function infiniteQueryOptions< > & { queryKey: DataTag, TError> } - -/** - * Allows to share and re-use infinite query options in a type-safe way. - * - * The `queryKey` will be tagged with the type from `queryFn`. - * @param options - The infinite query options to tag with the type from `queryFn`. - * @returns The tagged infinite query options. - */ export function infiniteQueryOptions(options: unknown) { return options } diff --git a/packages/angular-query-experimental/src/inject-infinite-query.ts b/packages/angular-query-experimental/src/inject-infinite-query.ts index ee6de032409..c7ce4eb9cae 100644 --- a/packages/angular-query-experimental/src/inject-infinite-query.ts +++ b/packages/angular-query-experimental/src/inject-infinite-query.ts @@ -9,6 +9,7 @@ import { createBaseQuery } from './create-base-query' import type { DefaultError, InfiniteData, + InfiniteQueryMode, QueryKey, QueryObserver, } from '@tanstack/query-core' @@ -19,6 +20,10 @@ import type { } from './types' import type { DefinedInitialDataInfiniteOptions, + DeclarativeDefinedInitialDataInfiniteOptions, + DeclarativeUndefinedInitialDataInfiniteOptions, + ImperativeDefinedInitialDataInfiniteOptions, + ImperativeUndefinedInitialDataInfiniteOptions, UndefinedInitialDataInfiniteOptions, } from './infinite-query-options' @@ -31,13 +36,75 @@ export interface InjectInfiniteQueryOptions { injector?: Injector } -/** - * Injects an infinite query: a declarative dependency on an asynchronous source of data that is tied to a unique key. - * Infinite queries can additively "load more" data onto an existing set of data or "infinite scroll" - * @param injectInfiniteQueryFn - A function that returns infinite query options. - * @param options - Additional configuration. - * @returns The infinite query result. - */ +export function injectInfiniteQuery< + TQueryFnData, + TError = DefaultError, + TData = InfiniteData, + TQueryKey extends QueryKey = QueryKey, + TPageParam = unknown, +>( + injectInfiniteQueryFn: () => ImperativeDefinedInitialDataInfiniteOptions< + TQueryFnData, + TError, + TData, + TQueryKey, + TPageParam + >, + options?: InjectInfiniteQueryOptions, +): DefinedCreateInfiniteQueryResult< + TData, + TError, + TPageParam, + InfiniteQueryMode +> +export function injectInfiniteQuery< + TQueryFnData, + TError = DefaultError, + TData = InfiniteData, + TQueryKey extends QueryKey = QueryKey, + TPageParam = unknown, +>( + injectInfiniteQueryFn: () => DeclarativeDefinedInitialDataInfiniteOptions< + TQueryFnData, + TError, + TData, + TQueryKey, + TPageParam + >, + options?: InjectInfiniteQueryOptions, +): DefinedCreateInfiniteQueryResult +export function injectInfiniteQuery< + TQueryFnData, + TError = DefaultError, + TData = InfiniteData, + TQueryKey extends QueryKey = QueryKey, + TPageParam = unknown, +>( + injectInfiniteQueryFn: () => ImperativeUndefinedInitialDataInfiniteOptions< + TQueryFnData, + TError, + TData, + TQueryKey, + TPageParam + >, + options?: InjectInfiniteQueryOptions, +): CreateInfiniteQueryResult +export function injectInfiniteQuery< + TQueryFnData, + TError = DefaultError, + TData = InfiniteData, + TQueryKey extends QueryKey = QueryKey, + TPageParam = unknown, +>( + injectInfiniteQueryFn: () => DeclarativeUndefinedInitialDataInfiniteOptions< + TQueryFnData, + TError, + TData, + TQueryKey, + TPageParam + >, + options?: InjectInfiniteQueryOptions, +): CreateInfiniteQueryResult export function injectInfiniteQuery< TQueryFnData, TError = DefaultError, @@ -53,15 +120,7 @@ export function injectInfiniteQuery< TPageParam >, options?: InjectInfiniteQueryOptions, -): DefinedCreateInfiniteQueryResult - -/** - * Injects an infinite query: a declarative dependency on an asynchronous source of data that is tied to a unique key. - * Infinite queries can additively "load more" data onto an existing set of data or "infinite scroll" - * @param injectInfiniteQueryFn - A function that returns infinite query options. - * @param options - Additional configuration. - * @returns The infinite query result. - */ +): DefinedCreateInfiniteQueryResult export function injectInfiniteQuery< TQueryFnData, TError = DefaultError, @@ -77,15 +136,7 @@ export function injectInfiniteQuery< TPageParam >, options?: InjectInfiniteQueryOptions, -): CreateInfiniteQueryResult - -/** - * Injects an infinite query: a declarative dependency on an asynchronous source of data that is tied to a unique key. - * Infinite queries can additively "load more" data onto an existing set of data or "infinite scroll" - * @param injectInfiniteQueryFn - A function that returns infinite query options. - * @param options - Additional configuration. - * @returns The infinite query result. - */ +): CreateInfiniteQueryResult export function injectInfiniteQuery< TQueryFnData, TError = DefaultError, @@ -101,7 +152,7 @@ export function injectInfiniteQuery< TPageParam >, options?: InjectInfiniteQueryOptions, -): CreateInfiniteQueryResult +): CreateInfiniteQueryResult /** * Injects an infinite query: a declarative dependency on an asynchronous source of data that is tied to a unique key. @@ -111,15 +162,15 @@ export function injectInfiniteQuery< * @returns The infinite query result. */ export function injectInfiniteQuery( - injectInfiniteQueryFn: () => CreateInfiniteQueryOptions, + injectInfiniteQueryFn: () => any, options?: InjectInfiniteQueryOptions, -) { +): any { !options?.injector && assertInInjectionContext(injectInfiniteQuery) - const injector = options?.injector ?? inject(Injector) - return runInInjectionContext(injector, () => + + return runInInjectionContext(options?.injector ?? inject(Injector), () => createBaseQuery( - injectInfiniteQueryFn, + injectInfiniteQueryFn as unknown as () => any, InfiniteQueryObserver as typeof QueryObserver, ), - ) + ) as any } diff --git a/packages/angular-query-experimental/src/types.ts b/packages/angular-query-experimental/src/types.ts index f8eec735c49..1e9a7717878 100644 --- a/packages/angular-query-experimental/src/types.ts +++ b/packages/angular-query-experimental/src/types.ts @@ -5,6 +5,7 @@ import type { DefinedInfiniteQueryObserverResult, DefinedQueryObserverResult, DistributiveOmit, + InfiniteQueryMode, InfiniteQueryObserverOptions, InfiniteQueryObserverResult, MutateFunction, @@ -79,13 +80,55 @@ export type CreateInfiniteQueryOptions< TData = TQueryFnData, TQueryKey extends QueryKey = QueryKey, TPageParam = unknown, + TMode extends InfiniteQueryMode | undefined = InfiniteQueryMode | undefined, +> = TMode extends InfiniteQueryMode + ? CreateImperativeInfiniteQueryOptions< + TQueryFnData, + TError, + TData, + TQueryKey, + TPageParam + > + : CreateDeclarativeInfiniteQueryOptions< + TQueryFnData, + TError, + TData, + TQueryKey, + TPageParam + > + +export type CreateDeclarativeInfiniteQueryOptions< + TQueryFnData = unknown, + TError = DefaultError, + TData = TQueryFnData, + TQueryKey extends QueryKey = QueryKey, + TPageParam = unknown, +> = DistributiveOmit< + InfiniteQueryObserverOptions< + TQueryFnData, + TError, + TData, + TQueryKey, + TPageParam, + undefined + >, + 'suspense' +> + +export type CreateImperativeInfiniteQueryOptions< + TQueryFnData = unknown, + TError = DefaultError, + TData = TQueryFnData, + TQueryKey extends QueryKey = QueryKey, + TPageParam = unknown, > = DistributiveOmit< InfiniteQueryObserverOptions< TQueryFnData, TError, TData, TQueryKey, - TPageParam + TPageParam, + InfiniteQueryMode >, 'suspense' > @@ -112,15 +155,21 @@ export type DefinedCreateQueryResult< export type CreateInfiniteQueryResult< TData = unknown, TError = DefaultError, + TPageParam = unknown, + TMode extends InfiniteQueryMode | undefined = undefined, > = BaseQueryNarrowing & - MapToSignals> + MapToSignals> export type DefinedCreateInfiniteQueryResult< TData = unknown, TError = DefaultError, + TPageParam = unknown, + TMode extends InfiniteQueryMode | undefined = undefined, TDefinedInfiniteQueryObserver = DefinedInfiniteQueryObserverResult< TData, - TError + TError, + TPageParam, + TMode >, > = MapToSignals From 5c61412daae9783c583a9ccf9eeecd4d8340a45f Mon Sep 17 00:00:00 2001 From: TkDodo Date: Fri, 3 Apr 2026 20:01:48 +0200 Subject: [PATCH 15/25] manual mode --- .../infinite-query-options.test-d.ts | 24 +++++++-------- .../__tests__/inject-infinite-query.test-d.ts | 6 ++-- .../src/infinite-query-options.ts | 28 ++++++++--------- .../src/inject-infinite-query.ts | 8 ++--- .../angular-query-experimental/src/types.ts | 4 +-- .../__tests__/infiniteQueryBehavior.test.tsx | 8 ++--- .../infiniteQueryObserver.test-d.tsx | 24 +++++++-------- .../src/__tests__/queryClient.test-d.tsx | 14 ++++----- .../src/__tests__/queryClient.test.tsx | 12 ++++---- .../query-core/src/infiniteQueryBehavior.ts | 4 +-- packages/query-core/src/queryClient.ts | 8 ++--- packages/query-core/src/types.ts | 14 ++++----- .../__tests__/infiniteQueryOptions.test-d.tsx | 18 +++++------ .../react-query/src/__tests__/ssr.test.tsx | 2 +- .../src/__tests__/useInfiniteQuery.test-d.tsx | 10 +++---- .../react-query/src/infiniteQueryOptions.ts | 30 +++++++++---------- packages/react-query/src/types.ts | 8 ++--- packages/react-query/src/useInfiniteQuery.ts | 8 ++--- .../src/__tests__/useQueryOptions.test-d.tsx | 6 ++-- .../tests/infiniteQueryOptions.test-d.ts | 6 ++-- .../__tests__/infiniteQueryOptions.test-d.ts | 6 ++-- 21 files changed, 124 insertions(+), 124 deletions(-) diff --git a/packages/angular-query-experimental/src/__tests__/infinite-query-options.test-d.ts b/packages/angular-query-experimental/src/__tests__/infinite-query-options.test-d.ts index 3c40189cf39..ecd2013b0b1 100644 --- a/packages/angular-query-experimental/src/__tests__/infinite-query-options.test-d.ts +++ b/packages/angular-query-experimental/src/__tests__/infinite-query-options.test-d.ts @@ -39,7 +39,7 @@ describe('infiniteQueryOptions', () => { queryKey: ['key'], queryFn: () => Promise.resolve('string'), initialPageParam: 1, - mode: 'imperative', + mode: 'manual', }) const { data, fetchNextPage } = injectInfiniteQuery(() => options) @@ -50,11 +50,11 @@ describe('infiniteQueryOptions', () => { >() fetchNextPage({ pageParam: 2 }) - // @ts-expect-error pageParam is required in imperative mode + // @ts-expect-error pageParam is required in manual mode fetchNextPage() }) - it('should preserve imperative fetch method types', () => { + it('should preserve manual fetch method types', () => { const options = infiniteQueryOptions({ queryKey: ['key'], queryFn: ({ pageParam }) => { @@ -62,7 +62,7 @@ describe('infiniteQueryOptions', () => { return pageParam * 5 }, initialPageParam: 1, - mode: 'imperative', + mode: 'manual', }) const { fetchNextPage, fetchPreviousPage } = injectInfiniteQuery(() => options) @@ -70,7 +70,7 @@ describe('infiniteQueryOptions', () => { fetchNextPage({ pageParam: 2 }) fetchPreviousPage({ pageParam: 0 }) - // @ts-expect-error pageParam is required in imperative mode + // @ts-expect-error pageParam is required in manual mode fetchNextPage() }) @@ -79,7 +79,7 @@ describe('infiniteQueryOptions', () => { queryKey: ['key'], queryFn: () => Promise.resolve('string'), initialPageParam: 1, - mode: 'imperative', + mode: 'manual', }) const data = await new QueryClient().fetchInfiniteQuery(options) @@ -179,29 +179,29 @@ describe('infiniteQueryOptions', () => { ) }) - it('should reject missing mode / getNextPageParam and reject getters in imperative mode', () => { - // @ts-expect-error getNextPageParam is required unless mode is imperative + it('should reject missing mode / getNextPageParam and reject getters in manual mode', () => { + // @ts-expect-error getNextPageParam is required unless mode is manual infiniteQueryOptions({ queryKey: ['key'], queryFn: () => Promise.resolve('string'), initialPageParam: 1, }) - // @ts-expect-error getNextPageParam is not allowed in imperative mode + // @ts-expect-error getNextPageParam is not allowed in manual mode infiniteQueryOptions({ queryKey: ['key'], queryFn: () => Promise.resolve('string'), initialPageParam: 1, - mode: 'imperative', + mode: 'manual', getNextPageParam: () => 1, }) - // @ts-expect-error getPreviousPageParam is not allowed in imperative mode + // @ts-expect-error getPreviousPageParam is not allowed in manual mode infiniteQueryOptions({ queryKey: ['key'], queryFn: () => Promise.resolve('string'), initialPageParam: 1, - mode: 'imperative', + mode: 'manual', getPreviousPageParam: () => 0, }) }) diff --git a/packages/angular-query-experimental/src/__tests__/inject-infinite-query.test-d.ts b/packages/angular-query-experimental/src/__tests__/inject-infinite-query.test-d.ts index 388511aeefc..655dbec2cf2 100644 --- a/packages/angular-query-experimental/src/__tests__/inject-infinite-query.test-d.ts +++ b/packages/angular-query-experimental/src/__tests__/inject-infinite-query.test-d.ts @@ -40,20 +40,20 @@ describe('injectInfiniteQuery', () => { } }) - test('should require pageParam on imperative fetch methods', () => { + test('should require pageParam on manual fetch methods', () => { const query = TestBed.runInInjectionContext(() => { return injectInfiniteQuery(() => ({ queryKey: ['infiniteQuery'], queryFn: ({ pageParam }) => 'data on page ' + pageParam, initialPageParam: 0, - mode: 'imperative', + mode: 'manual', })) }) query.fetchNextPage({ pageParam: 1 }) query.fetchPreviousPage({ pageParam: 0 }) - // @ts-expect-error pageParam is required in imperative mode + // @ts-expect-error pageParam is required in manual mode query.fetchNextPage() }) }) diff --git a/packages/angular-query-experimental/src/infinite-query-options.ts b/packages/angular-query-experimental/src/infinite-query-options.ts index fd412dc0f5b..fafe8eec0bb 100644 --- a/packages/angular-query-experimental/src/infinite-query-options.ts +++ b/packages/angular-query-experimental/src/infinite-query-options.ts @@ -11,7 +11,7 @@ import type { } from '@tanstack/query-core' import type { CreateDeclarativeInfiniteQueryOptions, - CreateImperativeInfiniteQueryOptions, + CreateManualInfiniteQueryOptions, CreateInfiniteQueryOptions, } from './types' @@ -70,13 +70,13 @@ export type DeclarativeUndefinedInitialDataInfiniteOptions< > & OptionalInitialData -export type ImperativeUndefinedInitialDataInfiniteOptions< +export type ManualUndefinedInitialDataInfiniteOptions< TQueryFnData, TError = DefaultError, TData = InfiniteData, TQueryKey extends QueryKey = QueryKey, TPageParam = unknown, -> = CreateImperativeInfiniteQueryOptions< +> = CreateManualInfiniteQueryOptions< TQueryFnData, TError, TData, @@ -101,14 +101,14 @@ export type UnusedSkipTokenDeclarativeInfiniteOptions< > > -export type UnusedSkipTokenImperativeInfiniteOptions< +export type UnusedSkipTokenManualInfiniteOptions< TQueryFnData, TError = DefaultError, TData = InfiniteData, TQueryKey extends QueryKey = QueryKey, TPageParam = unknown, > = WithoutSkipTokenQueryFn< - CreateImperativeInfiniteQueryOptions< + CreateManualInfiniteQueryOptions< TQueryFnData, TError, TData, @@ -131,7 +131,7 @@ export type UnusedSkipTokenInfiniteOptions< TQueryKey, TPageParam > - | UnusedSkipTokenImperativeInfiniteOptions< + | UnusedSkipTokenManualInfiniteOptions< TQueryFnData, TError, TData, @@ -154,13 +154,13 @@ export type DeclarativeDefinedInitialDataInfiniteOptions< > & RequiredInitialData -export type ImperativeDefinedInitialDataInfiniteOptions< +export type ManualDefinedInitialDataInfiniteOptions< TQueryFnData, TError = DefaultError, TData = InfiniteData, TQueryKey extends QueryKey = QueryKey, TPageParam = unknown, -> = CreateImperativeInfiniteQueryOptions< +> = CreateManualInfiniteQueryOptions< TQueryFnData, TError, TData, @@ -216,14 +216,14 @@ export function infiniteQueryOptions< TQueryKey extends QueryKey = QueryKey, TPageParam = unknown, >( - options: ImperativeDefinedInitialDataInfiniteOptions< + options: ManualDefinedInitialDataInfiniteOptions< TQueryFnData, TError, TData, TQueryKey, TPageParam >, -): ImperativeDefinedInitialDataInfiniteOptions< +): ManualDefinedInitialDataInfiniteOptions< TQueryFnData, TError, TData, @@ -262,14 +262,14 @@ export function infiniteQueryOptions< TQueryKey extends QueryKey = QueryKey, TPageParam = unknown, >( - options: UnusedSkipTokenImperativeInfiniteOptions< + options: UnusedSkipTokenManualInfiniteOptions< TQueryFnData, TError, TData, TQueryKey, TPageParam >, -): UnusedSkipTokenImperativeInfiniteOptions< +): UnusedSkipTokenManualInfiniteOptions< TQueryFnData, TError, TData, @@ -308,14 +308,14 @@ export function infiniteQueryOptions< TQueryKey extends QueryKey = QueryKey, TPageParam = unknown, >( - options: ImperativeUndefinedInitialDataInfiniteOptions< + options: ManualUndefinedInitialDataInfiniteOptions< TQueryFnData, TError, TData, TQueryKey, TPageParam >, -): ImperativeUndefinedInitialDataInfiniteOptions< +): ManualUndefinedInitialDataInfiniteOptions< TQueryFnData, TError, TData, diff --git a/packages/angular-query-experimental/src/inject-infinite-query.ts b/packages/angular-query-experimental/src/inject-infinite-query.ts index c7ce4eb9cae..90f8dd84e5d 100644 --- a/packages/angular-query-experimental/src/inject-infinite-query.ts +++ b/packages/angular-query-experimental/src/inject-infinite-query.ts @@ -22,8 +22,8 @@ import type { DefinedInitialDataInfiniteOptions, DeclarativeDefinedInitialDataInfiniteOptions, DeclarativeUndefinedInitialDataInfiniteOptions, - ImperativeDefinedInitialDataInfiniteOptions, - ImperativeUndefinedInitialDataInfiniteOptions, + ManualDefinedInitialDataInfiniteOptions, + ManualUndefinedInitialDataInfiniteOptions, UndefinedInitialDataInfiniteOptions, } from './infinite-query-options' @@ -43,7 +43,7 @@ export function injectInfiniteQuery< TQueryKey extends QueryKey = QueryKey, TPageParam = unknown, >( - injectInfiniteQueryFn: () => ImperativeDefinedInitialDataInfiniteOptions< + injectInfiniteQueryFn: () => ManualDefinedInitialDataInfiniteOptions< TQueryFnData, TError, TData, @@ -80,7 +80,7 @@ export function injectInfiniteQuery< TQueryKey extends QueryKey = QueryKey, TPageParam = unknown, >( - injectInfiniteQueryFn: () => ImperativeUndefinedInitialDataInfiniteOptions< + injectInfiniteQueryFn: () => ManualUndefinedInitialDataInfiniteOptions< TQueryFnData, TError, TData, diff --git a/packages/angular-query-experimental/src/types.ts b/packages/angular-query-experimental/src/types.ts index 1e9a7717878..92def8164fe 100644 --- a/packages/angular-query-experimental/src/types.ts +++ b/packages/angular-query-experimental/src/types.ts @@ -82,7 +82,7 @@ export type CreateInfiniteQueryOptions< TPageParam = unknown, TMode extends InfiniteQueryMode | undefined = InfiniteQueryMode | undefined, > = TMode extends InfiniteQueryMode - ? CreateImperativeInfiniteQueryOptions< + ? CreateManualInfiniteQueryOptions< TQueryFnData, TError, TData, @@ -115,7 +115,7 @@ export type CreateDeclarativeInfiniteQueryOptions< 'suspense' > -export type CreateImperativeInfiniteQueryOptions< +export type CreateManualInfiniteQueryOptions< TQueryFnData = unknown, TError = DefaultError, TData = TQueryFnData, diff --git a/packages/query-core/src/__tests__/infiniteQueryBehavior.test.tsx b/packages/query-core/src/__tests__/infiniteQueryBehavior.test.tsx index 79408e21b49..d607e34e624 100644 --- a/packages/query-core/src/__tests__/infiniteQueryBehavior.test.tsx +++ b/packages/query-core/src/__tests__/infiniteQueryBehavior.test.tsx @@ -70,7 +70,7 @@ describe('InfiniteQueryBehavior', () => { InfiniteData, Error, number, - 'imperative' + 'manual' > | undefined @@ -213,11 +213,11 @@ describe('InfiniteQueryBehavior', () => { InfiniteData, typeof key, number, - 'imperative' + 'manual' >(queryClient, { queryKey: key, queryFn, - mode: 'imperative', + mode: 'manual', initialPageParam: 0, }) @@ -226,7 +226,7 @@ describe('InfiniteQueryBehavior', () => { InfiniteData, Error, number, - 'imperative' + 'manual' > | undefined diff --git a/packages/query-core/src/__tests__/infiniteQueryObserver.test-d.tsx b/packages/query-core/src/__tests__/infiniteQueryObserver.test-d.tsx index 90b8d110ac0..3b832065b3f 100644 --- a/packages/query-core/src/__tests__/infiniteQueryObserver.test-d.tsx +++ b/packages/query-core/src/__tests__/infiniteQueryObserver.test-d.tsx @@ -98,26 +98,26 @@ describe('InfiniteQueryObserver', () => { InfiniteData, ReturnType, number, - 'imperative' + 'manual' >(queryClient, { queryKey: queryKey(), queryFn: ({ pageParam }) => String(pageParam), - mode: 'imperative', + mode: 'manual', initialPageParam: 1, }) observer.fetchNextPage({ pageParam: 2 }) observer.fetchPreviousPage({ pageParam: 0, cancelRefetch: false }) - // @ts-expect-error pageParam is required in imperative mode + // @ts-expect-error pageParam is required in manual mode observer.fetchNextPage() - // @ts-expect-error pageParam is required in imperative mode + // @ts-expect-error pageParam is required in manual mode observer.fetchPreviousPage() }) it('should reject missing mode / getNextPageParam', () => { - // @ts-expect-error getNextPageParam is required unless mode is imperative + // @ts-expect-error getNextPageParam is required unless mode is manual new InfiniteQueryObserver(queryClient, { queryKey: queryKey(), queryFn: ({ pageParam }) => String(pageParam), @@ -125,35 +125,35 @@ describe('InfiniteQueryObserver', () => { }) }) - it('should reject page param getters in imperative mode', () => { - // @ts-expect-error getNextPageParam is not allowed in imperative mode + it('should reject page param getters in manual mode', () => { + // @ts-expect-error getNextPageParam is not allowed in manual mode new InfiniteQueryObserver< string, Error, InfiniteData, ReturnType, number, - 'imperative' + 'manual' >(queryClient, { queryKey: queryKey(), queryFn: ({ pageParam }) => String(pageParam), - mode: 'imperative', + mode: 'manual', initialPageParam: 1, getNextPageParam: () => 2, }) - // @ts-expect-error getPreviousPageParam is not allowed in imperative mode + // @ts-expect-error getPreviousPageParam is not allowed in manual mode new InfiniteQueryObserver< string, Error, InfiniteData, ReturnType, number, - 'imperative' + 'manual' >(queryClient, { queryKey: queryKey(), queryFn: ({ pageParam }) => String(pageParam), - mode: 'imperative', + mode: 'manual', initialPageParam: 1, getPreviousPageParam: () => 0, }) diff --git a/packages/query-core/src/__tests__/queryClient.test-d.tsx b/packages/query-core/src/__tests__/queryClient.test-d.tsx index d8d66df86db..249efab5772 100644 --- a/packages/query-core/src/__tests__/queryClient.test-d.tsx +++ b/packages/query-core/src/__tests__/queryClient.test-d.tsx @@ -189,30 +189,30 @@ describe('fetchInfiniteQuery', () => { }) }) - it('should allow imperative mode without page param getters', () => { + it('should allow manual mode without page param getters', () => { void new QueryClient().fetchInfiniteQuery({ queryKey: ['key'], queryFn: () => Promise.resolve('string'), - mode: 'imperative', + mode: 'manual', initialPageParam: 1, }) }) - it('should not allow imperative mode with page param getters or pages', () => { - // @ts-expect-error getNextPageParam is not allowed in imperative mode + it('should not allow manual mode with page param getters or pages', () => { + // @ts-expect-error getNextPageParam is not allowed in manual mode void new QueryClient().fetchInfiniteQuery({ queryKey: ['key'], queryFn: () => Promise.resolve('string'), - mode: 'imperative', + mode: 'manual', initialPageParam: 1, getNextPageParam: () => 1, }) - // @ts-expect-error pages are not allowed in imperative mode + // @ts-expect-error pages are not allowed in manual mode void new QueryClient().fetchInfiniteQuery({ queryKey: ['key'], queryFn: () => Promise.resolve('string'), - mode: 'imperative', + mode: 'manual', initialPageParam: 1, pages: 2, }) diff --git a/packages/query-core/src/__tests__/queryClient.test.tsx b/packages/query-core/src/__tests__/queryClient.test.tsx index 16ec4bace67..03f1ba853a5 100644 --- a/packages/query-core/src/__tests__/queryClient.test.tsx +++ b/packages/query-core/src/__tests__/queryClient.test.tsx @@ -814,7 +814,7 @@ describe('queryClient', () => { queryKey: key, queryFn: fetchFn, initialPageParam: 0, - mode: 'imperative', + mode: 'manual', }), ).resolves.toEqual(data) }) @@ -824,7 +824,7 @@ describe('queryClient', () => { const result = await queryClient.fetchInfiniteQuery({ queryKey: key, initialPageParam: 10, - mode: 'imperative', + mode: 'manual', queryFn: ({ pageParam }) => Number(pageParam), }) const result2 = queryClient.getQueryData(key) @@ -858,7 +858,7 @@ describe('queryClient', () => { queryKey: key, queryFn: fetchFn, initialPageParam: 0, - mode: 'imperative', + mode: 'manual', }) const result = queryClient.getQueryData(key) @@ -876,7 +876,7 @@ describe('queryClient', () => { queryKey: key, queryFn: ({ pageParam }) => Number(pageParam), initialPageParam: 10, - mode: 'imperative', + mode: 'manual', }) const result = queryClient.getQueryData(key) @@ -1017,7 +1017,7 @@ describe('queryClient', () => { await queryClient.cancelQueries() - // with previous data present, imperative fetch should resolve to that data after cancel + // with previous data present, manual fetch should resolve to that data after cancel await expect(pending).resolves.toBe('data') const state1 = queryClient.getQueryState(key1) @@ -1045,7 +1045,7 @@ describe('queryClient', () => { }) }) - test('should throw CancelledError for imperative methods when initial fetch is cancelled', async () => { + test('should throw CancelledError for manual methods when initial fetch is cancelled', async () => { const key = queryKey() const promise = queryClient.fetchQuery({ diff --git a/packages/query-core/src/infiniteQueryBehavior.ts b/packages/query-core/src/infiniteQueryBehavior.ts index a56c25085bf..5b1188aff2d 100644 --- a/packages/query-core/src/infiniteQueryBehavior.ts +++ b/packages/query-core/src/infiniteQueryBehavior.ts @@ -7,7 +7,7 @@ import { import type { InfiniteData, InfiniteQueryPageParamsDeclarativeOptions, - InfiniteQueryPageParamsImperativeOptions, + InfiniteQueryPageParamsManualOptions, InfiniteQueryPageParamsOptions, OmitKeyof, QueryFunctionContext, @@ -21,7 +21,7 @@ export function infiniteQueryBehavior( return { onFetch: (context, query) => { const options = context.options as - | InfiniteQueryPageParamsImperativeOptions + | InfiniteQueryPageParamsManualOptions | InfiniteQueryPageParamsDeclarativeOptions const fetchMore = context.fetchOptions?.meta?.fetchMore const oldPages = context.state.data?.pages || [] diff --git a/packages/query-core/src/queryClient.ts b/packages/query-core/src/queryClient.ts index a6d25bf8a8c..b08f6990ba3 100644 --- a/packages/query-core/src/queryClient.ts +++ b/packages/query-core/src/queryClient.ts @@ -121,7 +121,7 @@ export class QueryClient { } /** - * Imperative (non-reactive) way to retrieve data for a QueryKey. + * Direct (non-reactive) way to retrieve data for a QueryKey. * Should only be used in callbacks or functions where reading the latest data is necessary, e.g. for optimistic updates. * * Hint: Do not use this function inside a component, because it won't receive updates. @@ -410,7 +410,7 @@ export class QueryClient { TData, TQueryKey, TPageParam, - 'imperative' + 'manual' >, ): Promise> fetchInfiniteQuery< @@ -468,7 +468,7 @@ export class QueryClient { TData, TQueryKey, TPageParam, - 'imperative' + 'manual' >, ): Promise prefetchInfiniteQuery< @@ -520,7 +520,7 @@ export class QueryClient { TData, TQueryKey, TPageParam, - 'imperative' + 'manual' >, ): Promise> ensureInfiniteQueryData< diff --git a/packages/query-core/src/types.ts b/packages/query-core/src/types.ts index 37fd193da68..b451b6e1553 100644 --- a/packages/query-core/src/types.ts +++ b/packages/query-core/src/types.ts @@ -281,7 +281,7 @@ export interface InitialPageParam { initialPageParam: TPageParam } -export type InfiniteQueryMode = 'imperative' +export type InfiniteQueryMode = 'manual' export interface InfiniteQueryPageParamsDeclarativeOptions< TQueryFnData = unknown, @@ -300,7 +300,7 @@ export interface InfiniteQueryPageParamsDeclarativeOptions< getNextPageParam: GetNextPageParamFunction } -export interface InfiniteQueryPageParamsImperativeOptions< +export interface InfiniteQueryPageParamsManualOptions< TPageParam = unknown, > extends InitialPageParam { mode: InfiniteQueryMode @@ -313,14 +313,14 @@ export type InfiniteQueryPageParamsOptions< TPageParam = unknown, TMode extends InfiniteQueryMode | undefined = undefined, > = TMode extends InfiniteQueryMode - ? InfiniteQueryPageParamsImperativeOptions + ? InfiniteQueryPageParamsManualOptions : InfiniteQueryPageParamsDeclarativeOptions export type FetchPageDirectionMode = InfiniteQueryMode | undefined -export interface ImperativeFetchPageOptions { +export interface ManualFetchPageOptions { /** - * The page param to pass to the query function for this imperative fetch. + * The page param to pass to the query function for this manual fetch. */ pageParam: TPageParam } @@ -670,14 +670,14 @@ export type InfiniteQueryFetchNextPageOptions< TPageParam = unknown, TMode extends FetchPageDirectionMode = undefined, > = TMode extends InfiniteQueryMode - ? ImperativeFetchPageOptions & FetchNextPageOptions + ? ManualFetchPageOptions & FetchNextPageOptions : FetchNextPageOptions export type InfiniteQueryFetchPreviousPageOptions< TPageParam = unknown, TMode extends FetchPageDirectionMode = undefined, > = TMode extends InfiniteQueryMode - ? ImperativeFetchPageOptions & FetchPreviousPageOptions + ? ManualFetchPageOptions & FetchPreviousPageOptions : FetchPreviousPageOptions export type InfiniteQueryFetchNextPageArgs< diff --git a/packages/react-query/src/__tests__/infiniteQueryOptions.test-d.tsx b/packages/react-query/src/__tests__/infiniteQueryOptions.test-d.tsx index 67d709e4394..afcd4db2720 100644 --- a/packages/react-query/src/__tests__/infiniteQueryOptions.test-d.tsx +++ b/packages/react-query/src/__tests__/infiniteQueryOptions.test-d.tsx @@ -40,7 +40,7 @@ describe('infiniteQueryOptions', () => { queryKey: ['key'], queryFn: () => Promise.resolve('string'), initialPageParam: 1, - mode: 'imperative', + mode: 'manual', }) const { data, fetchNextPage, fetchPreviousPage } = useInfiniteQuery(options) @@ -52,7 +52,7 @@ describe('infiniteQueryOptions', () => { fetchNextPage({ pageParam: 2 }) fetchPreviousPage({ pageParam: 0 }) - // @ts-expect-error pageParam is required in imperative mode + // @ts-expect-error pageParam is required in manual mode fetchNextPage() }) it('should work when passed to useSuspenseInfiniteQuery', () => { @@ -72,7 +72,7 @@ describe('infiniteQueryOptions', () => { queryKey: ['key'], queryFn: () => Promise.resolve('string'), initialPageParam: 1, - mode: 'imperative', + mode: 'manual', }) const data = await new QueryClient().fetchInfiniteQuery(options) @@ -158,29 +158,29 @@ describe('infiniteQueryOptions', () => { expectTypeOf(data).toEqualTypeOf>() }) - it('should reject missing mode / getNextPageParam and reject getters in imperative mode', () => { - // @ts-expect-error getNextPageParam is required unless mode is imperative + it('should reject missing mode / getNextPageParam and reject getters in manual mode', () => { + // @ts-expect-error getNextPageParam is required unless mode is manual infiniteQueryOptions({ queryKey: ['key'], queryFn: () => Promise.resolve('string'), initialPageParam: 1, }) - // @ts-expect-error getNextPageParam is not allowed in imperative mode + // @ts-expect-error getNextPageParam is not allowed in manual mode infiniteQueryOptions({ queryKey: ['key'], queryFn: () => Promise.resolve('string'), initialPageParam: 1, - mode: 'imperative', + mode: 'manual', getNextPageParam: () => 1, }) - // @ts-expect-error getPreviousPageParam is not allowed in imperative mode + // @ts-expect-error getPreviousPageParam is not allowed in manual mode infiniteQueryOptions({ queryKey: ['key'], queryFn: () => Promise.resolve('string'), initialPageParam: 1, - mode: 'imperative', + mode: 'manual', getPreviousPageParam: () => 0, }) }) diff --git a/packages/react-query/src/__tests__/ssr.test.tsx b/packages/react-query/src/__tests__/ssr.test.tsx index bdb3f090257..7c084a39df4 100644 --- a/packages/react-query/src/__tests__/ssr.test.tsx +++ b/packages/react-query/src/__tests__/ssr.test.tsx @@ -156,7 +156,7 @@ describe('Server Side Rendering', () => { queryKey: key, queryFn, initialPageParam: 0, - mode: 'imperative', + mode: 'manual', }) await vi.advanceTimersByTimeAsync(10) diff --git a/packages/react-query/src/__tests__/useInfiniteQuery.test-d.tsx b/packages/react-query/src/__tests__/useInfiniteQuery.test-d.tsx index 6420b4c9579..d7defd44761 100644 --- a/packages/react-query/src/__tests__/useInfiniteQuery.test-d.tsx +++ b/packages/react-query/src/__tests__/useInfiniteQuery.test-d.tsx @@ -34,7 +34,7 @@ describe('pageParam', () => { expectTypeOf(pageParam).toEqualTypeOf() }, initialPageParam: 1, - mode: 'imperative', + mode: 'manual', }) }) @@ -46,11 +46,11 @@ describe('pageParam', () => { expectTypeOf(pageParam).toEqualTypeOf() }, initialPageParam: 1, - mode: 'imperative', + mode: 'manual', }) }) - it('should require pageParam on imperative fetch methods', () => { + it('should require pageParam on manual fetch methods', () => { const infiniteQuery = useInfiniteQuery({ queryKey: ['key'], queryFn: ({ pageParam }) => { @@ -58,13 +58,13 @@ describe('pageParam', () => { return pageParam * 5 }, initialPageParam: 1, - mode: 'imperative', + mode: 'manual', }) infiniteQuery.fetchNextPage({ pageParam: 2 }) infiniteQuery.fetchPreviousPage({ pageParam: 0 }) - // @ts-expect-error pageParam is required in imperative mode + // @ts-expect-error pageParam is required in manual mode infiniteQuery.fetchNextPage() }) }) diff --git a/packages/react-query/src/infiniteQueryOptions.ts b/packages/react-query/src/infiniteQueryOptions.ts index 6124e709b8b..f88f2540055 100644 --- a/packages/react-query/src/infiniteQueryOptions.ts +++ b/packages/react-query/src/infiniteQueryOptions.ts @@ -10,7 +10,7 @@ import type { } from '@tanstack/query-core' import type { UseDeclarativeInfiniteQueryOptions, - UseImperativeInfiniteQueryOptions, + UseManualInfiniteQueryOptions, UseInfiniteQueryOptions, } from './types' @@ -82,13 +82,13 @@ export type DeclarativeUndefinedInitialDataInfiniteOptions< > & OptionalInitialData -export type ImperativeUndefinedInitialDataInfiniteOptions< +export type ManualUndefinedInitialDataInfiniteOptions< TQueryFnData, TError = DefaultError, TData = InfiniteData, TQueryKey extends QueryKey = QueryKey, TPageParam = unknown, -> = UseImperativeInfiniteQueryOptions< +> = UseManualInfiniteQueryOptions< TQueryFnData, TError, TData, @@ -113,14 +113,14 @@ export type UnusedSkipTokenDeclarativeInfiniteOptions< > > -export type UnusedSkipTokenImperativeInfiniteOptions< +export type UnusedSkipTokenManualInfiniteOptions< TQueryFnData, TError = DefaultError, TData = InfiniteData, TQueryKey extends QueryKey = QueryKey, TPageParam = unknown, > = WithoutSkipTokenQueryFn< - UseImperativeInfiniteQueryOptions< + UseManualInfiniteQueryOptions< TQueryFnData, TError, TData, @@ -143,7 +143,7 @@ export type UnusedSkipTokenInfiniteOptions< TQueryKey, TPageParam > - | UnusedSkipTokenImperativeInfiniteOptions< + | UnusedSkipTokenManualInfiniteOptions< TQueryFnData, TError, TData, @@ -165,7 +165,7 @@ export type DefinedInitialDataInfiniteOptions< TQueryKey, TPageParam > - | ImperativeDefinedInitialDataInfiniteOptions< + | ManualDefinedInitialDataInfiniteOptions< TQueryFnData, TError, TData, @@ -188,13 +188,13 @@ export type DeclarativeDefinedInitialDataInfiniteOptions< > & RequiredInitialData -export type ImperativeDefinedInitialDataInfiniteOptions< +export type ManualDefinedInitialDataInfiniteOptions< TQueryFnData, TError = DefaultError, TData = InfiniteData, TQueryKey extends QueryKey = QueryKey, TPageParam = unknown, -> = UseImperativeInfiniteQueryOptions< +> = UseManualInfiniteQueryOptions< TQueryFnData, TError, TData, @@ -233,14 +233,14 @@ export function infiniteQueryOptions< TQueryKey extends QueryKey = QueryKey, TPageParam = unknown, >( - options: ImperativeDefinedInitialDataInfiniteOptions< + options: ManualDefinedInitialDataInfiniteOptions< TQueryFnData, TError, TData, TQueryKey, TPageParam >, -): ImperativeDefinedInitialDataInfiniteOptions< +): ManualDefinedInitialDataInfiniteOptions< TQueryFnData, TError, TData, @@ -279,14 +279,14 @@ export function infiniteQueryOptions< TQueryKey extends QueryKey = QueryKey, TPageParam = unknown, >( - options: UnusedSkipTokenImperativeInfiniteOptions< + options: UnusedSkipTokenManualInfiniteOptions< TQueryFnData, TError, TData, TQueryKey, TPageParam >, -): UnusedSkipTokenImperativeInfiniteOptions< +): UnusedSkipTokenManualInfiniteOptions< TQueryFnData, TError, TData, @@ -325,14 +325,14 @@ export function infiniteQueryOptions< TQueryKey extends QueryKey = QueryKey, TPageParam = unknown, >( - options: ImperativeUndefinedInitialDataInfiniteOptions< + options: ManualUndefinedInitialDataInfiniteOptions< TQueryFnData, TError, TData, TQueryKey, TPageParam >, -): ImperativeUndefinedInitialDataInfiniteOptions< +): ManualUndefinedInitialDataInfiniteOptions< TQueryFnData, TError, TData, diff --git a/packages/react-query/src/types.ts b/packages/react-query/src/types.ts index e504062f5c4..2adaa1fb9e9 100644 --- a/packages/react-query/src/types.ts +++ b/packages/react-query/src/types.ts @@ -137,7 +137,7 @@ export type UseDeclarativeInfiniteQueryOptions< undefined > -export type UseImperativeInfiniteQueryOptions< +export type UseManualInfiniteQueryOptions< TQueryFnData = unknown, TError = DefaultError, TData = TQueryFnData, @@ -166,7 +166,7 @@ export type UseInfiniteQueryOptions< TQueryKey, TPageParam > - | UseImperativeInfiniteQueryOptions< + | UseManualInfiniteQueryOptions< TQueryFnData, TError, TData, @@ -207,7 +207,7 @@ export type UseSuspenseInfiniteQueryOptions< > }) | (DistributiveOmit< - UseImperativeInfiniteQueryOptions< + UseManualInfiniteQueryOptions< TQueryFnData, TError, TData, @@ -221,7 +221,7 @@ export type UseSuspenseInfiniteQueryOptions< * Defaults to `true`. */ queryFn?: Exclude< - UseImperativeInfiniteQueryOptions< + UseManualInfiniteQueryOptions< TQueryFnData, TError, TData, diff --git a/packages/react-query/src/useInfiniteQuery.ts b/packages/react-query/src/useInfiniteQuery.ts index 7dc240baf77..4571c280409 100644 --- a/packages/react-query/src/useInfiniteQuery.ts +++ b/packages/react-query/src/useInfiniteQuery.ts @@ -17,8 +17,8 @@ import type { import type { DeclarativeDefinedInitialDataInfiniteOptions, DeclarativeUndefinedInitialDataInfiniteOptions, - ImperativeDefinedInitialDataInfiniteOptions, - ImperativeUndefinedInitialDataInfiniteOptions, + ManualDefinedInitialDataInfiniteOptions, + ManualUndefinedInitialDataInfiniteOptions, } from './infiniteQueryOptions' export function useInfiniteQuery< @@ -45,7 +45,7 @@ export function useInfiniteQuery< TQueryKey extends QueryKey = QueryKey, TPageParam = unknown, >( - options: ImperativeDefinedInitialDataInfiniteOptions< + options: ManualDefinedInitialDataInfiniteOptions< TQueryFnData, TError, TData, @@ -79,7 +79,7 @@ export function useInfiniteQuery< TQueryKey extends QueryKey = QueryKey, TPageParam = unknown, >( - options: ImperativeUndefinedInitialDataInfiniteOptions< + options: ManualUndefinedInitialDataInfiniteOptions< TQueryFnData, TError, TData, diff --git a/packages/solid-query/src/__tests__/useQueryOptions.test-d.tsx b/packages/solid-query/src/__tests__/useQueryOptions.test-d.tsx index 68e28eeadae..cb378fd0ba4 100644 --- a/packages/solid-query/src/__tests__/useQueryOptions.test-d.tsx +++ b/packages/solid-query/src/__tests__/useQueryOptions.test-d.tsx @@ -73,7 +73,7 @@ describe('infiniteQueryOptions', () => { >() }) - it('should preserve imperative fetch method types', () => { + it('should preserve manual fetch method types', () => { const options = infiniteQueryOptions({ queryKey: ['key'], queryFn: ({ pageParam }) => { @@ -81,7 +81,7 @@ describe('infiniteQueryOptions', () => { return pageParam * 5 }, initialPageParam: 1, - mode: 'imperative', + mode: 'manual', }) const query = useInfiniteQuery(() => options) @@ -89,7 +89,7 @@ describe('infiniteQueryOptions', () => { query.fetchNextPage({ pageParam: 2 }) query.fetchPreviousPage({ pageParam: 0 }) - // @ts-expect-error pageParam is required in imperative mode + // @ts-expect-error pageParam is required in manual mode query.fetchNextPage() }) }) diff --git a/packages/svelte-query/tests/infiniteQueryOptions.test-d.ts b/packages/svelte-query/tests/infiniteQueryOptions.test-d.ts index a2cdbf0daae..84c741d612b 100644 --- a/packages/svelte-query/tests/infiniteQueryOptions.test-d.ts +++ b/packages/svelte-query/tests/infiniteQueryOptions.test-d.ts @@ -57,7 +57,7 @@ describe('queryOptions', () => { expectTypeOf(data).toEqualTypeOf>() }) - test('Should preserve imperative fetch method types', () => { + test('Should preserve manual fetch method types', () => { const options = infiniteQueryOptions({ queryKey: ['key'], queryFn: ({ pageParam }) => { @@ -65,7 +65,7 @@ describe('queryOptions', () => { return pageParam * 5 }, initialPageParam: 1, - mode: 'imperative', + mode: 'manual', }) const query = createInfiniteQuery(() => options) @@ -73,7 +73,7 @@ describe('queryOptions', () => { query.fetchNextPage({ pageParam: 2 }) query.fetchPreviousPage({ pageParam: 0 }) - // @ts-expect-error pageParam is required in imperative mode + // @ts-expect-error pageParam is required in manual mode query.fetchNextPage() }) }) diff --git a/packages/vue-query/src/__tests__/infiniteQueryOptions.test-d.ts b/packages/vue-query/src/__tests__/infiniteQueryOptions.test-d.ts index df3ac49765d..5c26f9d8a24 100644 --- a/packages/vue-query/src/__tests__/infiniteQueryOptions.test-d.ts +++ b/packages/vue-query/src/__tests__/infiniteQueryOptions.test-d.ts @@ -111,7 +111,7 @@ describe('infiniteQueryOptions', () => { >() }) - it('should preserve imperative fetch method types', () => { + it('should preserve manual fetch method types', () => { const options = infiniteQueryOptions({ queryKey: ['key'], queryFn: ({ pageParam }) => { @@ -119,7 +119,7 @@ describe('infiniteQueryOptions', () => { return pageParam * 5 }, initialPageParam: 1, - mode: 'imperative', + mode: 'manual', }) const query = reactive(useInfiniteQuery(options)) @@ -127,7 +127,7 @@ describe('infiniteQueryOptions', () => { query.fetchNextPage({ pageParam: 2 }) query.fetchPreviousPage({ pageParam: 0 }) - // @ts-expect-error pageParam is required in imperative mode + // @ts-expect-error pageParam is required in manual mode query.fetchNextPage() }) }) From 5a52edae1523031756b5b2a9e8c2f6672e8a53b1 Mon Sep 17 00:00:00 2001 From: "autofix-ci[bot]" <114827586+autofix-ci[bot]@users.noreply.github.com> Date: Fri, 3 Apr 2026 18:03:04 +0000 Subject: [PATCH 16/25] ci: apply automated fixes --- .../infinite-query-options.test-d.ts | 4 +++- .../src/infinite-query-options.ts | 10 ++++------ packages/query-core/src/queryClient.ts | 4 +++- .../react-query/src/infiniteQueryOptions.ts | 20 ++++++------------- packages/solid-query/src/useInfiniteQuery.ts | 7 +------ .../svelte-query/src/createInfiniteQuery.ts | 15 ++------------ packages/vue-query/src/useInfiniteQuery.ts | 5 +---- 7 files changed, 20 insertions(+), 45 deletions(-) diff --git a/packages/angular-query-experimental/src/__tests__/infinite-query-options.test-d.ts b/packages/angular-query-experimental/src/__tests__/infinite-query-options.test-d.ts index ecd2013b0b1..5362c257e4b 100644 --- a/packages/angular-query-experimental/src/__tests__/infinite-query-options.test-d.ts +++ b/packages/angular-query-experimental/src/__tests__/infinite-query-options.test-d.ts @@ -65,7 +65,9 @@ describe('infiniteQueryOptions', () => { mode: 'manual', }) - const { fetchNextPage, fetchPreviousPage } = injectInfiniteQuery(() => options) + const { fetchNextPage, fetchPreviousPage } = injectInfiniteQuery( + () => options, + ) fetchNextPage({ pageParam: 2 }) fetchPreviousPage({ pageParam: 0 }) diff --git a/packages/angular-query-experimental/src/infinite-query-options.ts b/packages/angular-query-experimental/src/infinite-query-options.ts index fafe8eec0bb..aa695ac5c9c 100644 --- a/packages/angular-query-experimental/src/infinite-query-options.ts +++ b/packages/angular-query-experimental/src/infinite-query-options.ts @@ -31,12 +31,10 @@ type RequiredInitialData = { | undefined } -type WithoutSkipTokenQueryFn = OmitKeyof< - TOptions, - 'queryFn' -> & { - queryFn?: Exclude -} +type WithoutSkipTokenQueryFn = + OmitKeyof & { + queryFn?: Exclude + } export type UndefinedInitialDataInfiniteOptions< TQueryFnData, diff --git a/packages/query-core/src/queryClient.ts b/packages/query-core/src/queryClient.ts index b08f6990ba3..1708678abe5 100644 --- a/packages/query-core/src/queryClient.ts +++ b/packages/query-core/src/queryClient.ts @@ -488,7 +488,9 @@ export class QueryClient { TMode >, ): Promise { - return this.fetchInfiniteQuery(options as any).then(noop).catch(noop) + return this.fetchInfiniteQuery(options as any) + .then(noop) + .catch(noop) } ensureInfiniteQueryData< diff --git a/packages/react-query/src/infiniteQueryOptions.ts b/packages/react-query/src/infiniteQueryOptions.ts index f88f2540055..350ddb572fd 100644 --- a/packages/react-query/src/infiniteQueryOptions.ts +++ b/packages/react-query/src/infiniteQueryOptions.ts @@ -14,10 +14,7 @@ import type { UseInfiniteQueryOptions, } from './types' -type OptionalInitialData< - TQueryFnData, - TPageParam, -> = { +type OptionalInitialData = { initialData?: | undefined | NonUndefinedGuard> @@ -26,22 +23,17 @@ type OptionalInitialData< > } -type RequiredInitialData< - TQueryFnData, - TPageParam, -> = { +type RequiredInitialData = { initialData: | NonUndefinedGuard> | (() => NonUndefinedGuard>) | undefined } -type WithoutSkipTokenQueryFn = OmitKeyof< - TOptions, - 'queryFn' -> & { - queryFn?: Exclude -} +type WithoutSkipTokenQueryFn = + OmitKeyof & { + queryFn?: Exclude + } type TaggedInfiniteQueryOptions< TOptions, diff --git a/packages/solid-query/src/useInfiniteQuery.ts b/packages/solid-query/src/useInfiniteQuery.ts index 7861810ed29..1fce37c3fff 100644 --- a/packages/solid-query/src/useInfiniteQuery.ts +++ b/packages/solid-query/src/useInfiniteQuery.ts @@ -53,12 +53,7 @@ export function useInfiniteQuery< InfiniteQueryMode >, queryClient?: Accessor, -): DefinedUseInfiniteQueryResult< - TData, - TError, - TPageParam, - InfiniteQueryMode -> +): DefinedUseInfiniteQueryResult export function useInfiniteQuery< TQueryFnData, TError = DefaultError, diff --git a/packages/svelte-query/src/createInfiniteQuery.ts b/packages/svelte-query/src/createInfiniteQuery.ts index b81939ceea3..b234a7f31fb 100644 --- a/packages/svelte-query/src/createInfiniteQuery.ts +++ b/packages/svelte-query/src/createInfiniteQuery.ts @@ -52,25 +52,14 @@ export function createInfiniteQuery< > >, queryClient?: Accessor, -): CreateInfiniteQueryResult< - TData, - TError, - TPageParam, - InfiniteQueryMode -> +): CreateInfiniteQueryResult export function createInfiniteQuery( options: any, queryClient?: Accessor, ): any { return createBaseQuery( options as Accessor< - CreateBaseQueryOptions< - any, - any, - any, - InfiniteData, - any - > + CreateBaseQueryOptions, any> >, InfiniteQueryObserver as typeof QueryObserver, queryClient, diff --git a/packages/vue-query/src/useInfiniteQuery.ts b/packages/vue-query/src/useInfiniteQuery.ts index 8b12fb7fb5b..7f7056312cd 100644 --- a/packages/vue-query/src/useInfiniteQuery.ts +++ b/packages/vue-query/src/useInfiniteQuery.ts @@ -167,10 +167,7 @@ export function useInfiniteQuery< queryClient?: QueryClient, ): UseInfiniteQueryReturnType -export function useInfiniteQuery( - options: any, - queryClient?: QueryClient, -): any { +export function useInfiniteQuery(options: any, queryClient?: QueryClient): any { return useBaseQuery( InfiniteQueryObserver as typeof QueryObserver, options, From 68473713f008dd9d26f565ed6f810c51960892e1 Mon Sep 17 00:00:00 2001 From: TkDodo Date: Fri, 3 Apr 2026 20:07:47 +0200 Subject: [PATCH 17/25] bring back jsdocs --- .../src/inject-infinite-query.ts | 280 ++++++++++++++++++ 1 file changed, 280 insertions(+) diff --git a/packages/angular-query-experimental/src/inject-infinite-query.ts b/packages/angular-query-experimental/src/inject-infinite-query.ts index 90f8dd84e5d..3ba7da03360 100644 --- a/packages/angular-query-experimental/src/inject-infinite-query.ts +++ b/packages/angular-query-experimental/src/inject-infinite-query.ts @@ -36,6 +36,46 @@ export interface InjectInfiniteQueryOptions { injector?: Injector } +/** + * Injects an infinite query: a declarative dependency on an asynchronous source of data that is tied to a unique key. + * Infinite queries can additively "load more" data onto an existing set of data or "infinite scroll" + * + * **Basic example** + * ```ts + * class ServiceOrComponent { + * query = injectInfiniteQuery(() => ({ + * queryKey: ['projects'], + * queryFn: ({ pageParam }) => + * this.#http.get('/api/projects?cursor=' + pageParam), + * initialPageParam: 0, + * getNextPageParam: (lastPage) => lastPage.nextCursor, + * })) + * } + * ``` + * + * Similar to `computed` from Angular, the function passed to `injectInfiniteQuery` will be run in the reactive context. + * In the example below, the query will be automatically enabled and executed when the filter signal changes + * to a truthy value. When the filter signal changes back to a falsy value, the query will be disabled. + * + * **Reactive example** + * ```ts + * class ServiceOrComponent { + * filter = signal('') + * + * projectsQuery = injectInfiniteQuery(() => ({ + * queryKey: ['projects', this.filter()], + * queryFn: ({ pageParam }) => fetchProjects(this.filter(), pageParam), + * initialPageParam: 0, + * getNextPageParam: (lastPage) => lastPage.nextCursor, + * enabled: !!this.filter(), + * })) + * } + * ``` + * @param injectInfiniteQueryFn - A function that returns infinite query options. + * @param options - Additional configuration. + * @returns The infinite query result. + * @see https://tanstack.com/query/latest/docs/framework/angular/guides/infinite-queries + */ export function injectInfiniteQuery< TQueryFnData, TError = DefaultError, @@ -57,6 +97,46 @@ export function injectInfiniteQuery< TPageParam, InfiniteQueryMode > +/** + * Injects an infinite query: a declarative dependency on an asynchronous source of data that is tied to a unique key. + * Infinite queries can additively "load more" data onto an existing set of data or "infinite scroll" + * + * **Basic example** + * ```ts + * class ServiceOrComponent { + * query = injectInfiniteQuery(() => ({ + * queryKey: ['projects'], + * queryFn: ({ pageParam }) => + * this.#http.get('/api/projects?cursor=' + pageParam), + * initialPageParam: 0, + * getNextPageParam: (lastPage) => lastPage.nextCursor, + * })) + * } + * ``` + * + * Similar to `computed` from Angular, the function passed to `injectInfiniteQuery` will be run in the reactive context. + * In the example below, the query will be automatically enabled and executed when the filter signal changes + * to a truthy value. When the filter signal changes back to a falsy value, the query will be disabled. + * + * **Reactive example** + * ```ts + * class ServiceOrComponent { + * filter = signal('') + * + * projectsQuery = injectInfiniteQuery(() => ({ + * queryKey: ['projects', this.filter()], + * queryFn: ({ pageParam }) => fetchProjects(this.filter(), pageParam), + * initialPageParam: 0, + * getNextPageParam: (lastPage) => lastPage.nextCursor, + * enabled: !!this.filter(), + * })) + * } + * ``` + * @param injectInfiniteQueryFn - A function that returns infinite query options. + * @param options - Additional configuration. + * @returns The infinite query result. + * @see https://tanstack.com/query/latest/docs/framework/angular/guides/infinite-queries + */ export function injectInfiniteQuery< TQueryFnData, TError = DefaultError, @@ -73,6 +153,46 @@ export function injectInfiniteQuery< >, options?: InjectInfiniteQueryOptions, ): DefinedCreateInfiniteQueryResult +/** + * Injects an infinite query: a declarative dependency on an asynchronous source of data that is tied to a unique key. + * Infinite queries can additively "load more" data onto an existing set of data or "infinite scroll" + * + * **Basic example** + * ```ts + * class ServiceOrComponent { + * query = injectInfiniteQuery(() => ({ + * queryKey: ['projects'], + * queryFn: ({ pageParam }) => + * this.#http.get('/api/projects?cursor=' + pageParam), + * initialPageParam: 0, + * getNextPageParam: (lastPage) => lastPage.nextCursor, + * })) + * } + * ``` + * + * Similar to `computed` from Angular, the function passed to `injectInfiniteQuery` will be run in the reactive context. + * In the example below, the query will be automatically enabled and executed when the filter signal changes + * to a truthy value. When the filter signal changes back to a falsy value, the query will be disabled. + * + * **Reactive example** + * ```ts + * class ServiceOrComponent { + * filter = signal('') + * + * projectsQuery = injectInfiniteQuery(() => ({ + * queryKey: ['projects', this.filter()], + * queryFn: ({ pageParam }) => fetchProjects(this.filter(), pageParam), + * initialPageParam: 0, + * getNextPageParam: (lastPage) => lastPage.nextCursor, + * enabled: !!this.filter(), + * })) + * } + * ``` + * @param injectInfiniteQueryFn - A function that returns infinite query options. + * @param options - Additional configuration. + * @returns The infinite query result. + * @see https://tanstack.com/query/latest/docs/framework/angular/guides/infinite-queries + */ export function injectInfiniteQuery< TQueryFnData, TError = DefaultError, @@ -89,6 +209,46 @@ export function injectInfiniteQuery< >, options?: InjectInfiniteQueryOptions, ): CreateInfiniteQueryResult +/** + * Injects an infinite query: a declarative dependency on an asynchronous source of data that is tied to a unique key. + * Infinite queries can additively "load more" data onto an existing set of data or "infinite scroll" + * + * **Basic example** + * ```ts + * class ServiceOrComponent { + * query = injectInfiniteQuery(() => ({ + * queryKey: ['projects'], + * queryFn: ({ pageParam }) => + * this.#http.get('/api/projects?cursor=' + pageParam), + * initialPageParam: 0, + * getNextPageParam: (lastPage) => lastPage.nextCursor, + * })) + * } + * ``` + * + * Similar to `computed` from Angular, the function passed to `injectInfiniteQuery` will be run in the reactive context. + * In the example below, the query will be automatically enabled and executed when the filter signal changes + * to a truthy value. When the filter signal changes back to a falsy value, the query will be disabled. + * + * **Reactive example** + * ```ts + * class ServiceOrComponent { + * filter = signal('') + * + * projectsQuery = injectInfiniteQuery(() => ({ + * queryKey: ['projects', this.filter()], + * queryFn: ({ pageParam }) => fetchProjects(this.filter(), pageParam), + * initialPageParam: 0, + * getNextPageParam: (lastPage) => lastPage.nextCursor, + * enabled: !!this.filter(), + * })) + * } + * ``` + * @param injectInfiniteQueryFn - A function that returns infinite query options. + * @param options - Additional configuration. + * @returns The infinite query result. + * @see https://tanstack.com/query/latest/docs/framework/angular/guides/infinite-queries + */ export function injectInfiniteQuery< TQueryFnData, TError = DefaultError, @@ -105,6 +265,46 @@ export function injectInfiniteQuery< >, options?: InjectInfiniteQueryOptions, ): CreateInfiniteQueryResult +/** + * Injects an infinite query: a declarative dependency on an asynchronous source of data that is tied to a unique key. + * Infinite queries can additively "load more" data onto an existing set of data or "infinite scroll" + * + * **Basic example** + * ```ts + * class ServiceOrComponent { + * query = injectInfiniteQuery(() => ({ + * queryKey: ['projects'], + * queryFn: ({ pageParam }) => + * this.#http.get('/api/projects?cursor=' + pageParam), + * initialPageParam: 0, + * getNextPageParam: (lastPage) => lastPage.nextCursor, + * })) + * } + * ``` + * + * Similar to `computed` from Angular, the function passed to `injectInfiniteQuery` will be run in the reactive context. + * In the example below, the query will be automatically enabled and executed when the filter signal changes + * to a truthy value. When the filter signal changes back to a falsy value, the query will be disabled. + * + * **Reactive example** + * ```ts + * class ServiceOrComponent { + * filter = signal('') + * + * projectsQuery = injectInfiniteQuery(() => ({ + * queryKey: ['projects', this.filter()], + * queryFn: ({ pageParam }) => fetchProjects(this.filter(), pageParam), + * initialPageParam: 0, + * getNextPageParam: (lastPage) => lastPage.nextCursor, + * enabled: !!this.filter(), + * })) + * } + * ``` + * @param injectInfiniteQueryFn - A function that returns infinite query options. + * @param options - Additional configuration. + * @returns The infinite query result. + * @see https://tanstack.com/query/latest/docs/framework/angular/guides/infinite-queries + */ export function injectInfiniteQuery< TQueryFnData, TError = DefaultError, @@ -121,6 +321,46 @@ export function injectInfiniteQuery< >, options?: InjectInfiniteQueryOptions, ): DefinedCreateInfiniteQueryResult +/** + * Injects an infinite query: a declarative dependency on an asynchronous source of data that is tied to a unique key. + * Infinite queries can additively "load more" data onto an existing set of data or "infinite scroll" + * + * **Basic example** + * ```ts + * class ServiceOrComponent { + * query = injectInfiniteQuery(() => ({ + * queryKey: ['projects'], + * queryFn: ({ pageParam }) => + * this.#http.get('/api/projects?cursor=' + pageParam), + * initialPageParam: 0, + * getNextPageParam: (lastPage) => lastPage.nextCursor, + * })) + * } + * ``` + * + * Similar to `computed` from Angular, the function passed to `injectInfiniteQuery` will be run in the reactive context. + * In the example below, the query will be automatically enabled and executed when the filter signal changes + * to a truthy value. When the filter signal changes back to a falsy value, the query will be disabled. + * + * **Reactive example** + * ```ts + * class ServiceOrComponent { + * filter = signal('') + * + * projectsQuery = injectInfiniteQuery(() => ({ + * queryKey: ['projects', this.filter()], + * queryFn: ({ pageParam }) => fetchProjects(this.filter(), pageParam), + * initialPageParam: 0, + * getNextPageParam: (lastPage) => lastPage.nextCursor, + * enabled: !!this.filter(), + * })) + * } + * ``` + * @param injectInfiniteQueryFn - A function that returns infinite query options. + * @param options - Additional configuration. + * @returns The infinite query result. + * @see https://tanstack.com/query/latest/docs/framework/angular/guides/infinite-queries + */ export function injectInfiniteQuery< TQueryFnData, TError = DefaultError, @@ -137,6 +377,46 @@ export function injectInfiniteQuery< >, options?: InjectInfiniteQueryOptions, ): CreateInfiniteQueryResult +/** + * Injects an infinite query: a declarative dependency on an asynchronous source of data that is tied to a unique key. + * Infinite queries can additively "load more" data onto an existing set of data or "infinite scroll" + * + * **Basic example** + * ```ts + * class ServiceOrComponent { + * query = injectInfiniteQuery(() => ({ + * queryKey: ['projects'], + * queryFn: ({ pageParam }) => + * this.#http.get('/api/projects?cursor=' + pageParam), + * initialPageParam: 0, + * getNextPageParam: (lastPage) => lastPage.nextCursor, + * })) + * } + * ``` + * + * Similar to `computed` from Angular, the function passed to `injectInfiniteQuery` will be run in the reactive context. + * In the example below, the query will be automatically enabled and executed when the filter signal changes + * to a truthy value. When the filter signal changes back to a falsy value, the query will be disabled. + * + * **Reactive example** + * ```ts + * class ServiceOrComponent { + * filter = signal('') + * + * projectsQuery = injectInfiniteQuery(() => ({ + * queryKey: ['projects', this.filter()], + * queryFn: ({ pageParam }) => fetchProjects(this.filter(), pageParam), + * initialPageParam: 0, + * getNextPageParam: (lastPage) => lastPage.nextCursor, + * enabled: !!this.filter(), + * })) + * } + * ``` + * @param injectInfiniteQueryFn - A function that returns infinite query options. + * @param options - Additional configuration. + * @returns The infinite query result. + * @see https://tanstack.com/query/latest/docs/framework/angular/guides/infinite-queries + */ export function injectInfiniteQuery< TQueryFnData, TError = DefaultError, From 83513ae66e971770a99d129c8148441403e3e267 Mon Sep 17 00:00:00 2001 From: TkDodo Date: Fri, 3 Apr 2026 20:15:27 +0200 Subject: [PATCH 18/25] simplify types --- .../src/infinite-query-options.ts | 99 ++++---------- .../src/inject-infinite-query.ts | 22 ++- .../angular-query-experimental/src/types.ts | 34 ++--- .../react-query/src/infiniteQueryOptions.ts | 128 ++++++------------ packages/react-query/src/types.ts | 65 +++------ packages/react-query/src/useInfiniteQuery.ts | 14 +- 6 files changed, 113 insertions(+), 249 deletions(-) diff --git a/packages/angular-query-experimental/src/infinite-query-options.ts b/packages/angular-query-experimental/src/infinite-query-options.ts index aa695ac5c9c..e1b821b34c8 100644 --- a/packages/angular-query-experimental/src/infinite-query-options.ts +++ b/packages/angular-query-experimental/src/infinite-query-options.ts @@ -2,17 +2,16 @@ import type { DataTag, DefaultError, InfiniteData, - InitialDataFunction, InfiniteQueryMode, + InitialDataFunction, NonUndefinedGuard, OmitKeyof, QueryKey, SkipToken, } from '@tanstack/query-core' import type { - CreateDeclarativeInfiniteQueryOptions, - CreateManualInfiniteQueryOptions, CreateInfiniteQueryOptions, + CreateManualInfiniteQueryOptions, } from './types' type OptionalInitialData = { @@ -53,21 +52,6 @@ export type UndefinedInitialDataInfiniteOptions< > & OptionalInitialData -export type DeclarativeUndefinedInitialDataInfiniteOptions< - TQueryFnData, - TError = DefaultError, - TData = InfiniteData, - TQueryKey extends QueryKey = QueryKey, - TPageParam = unknown, -> = CreateDeclarativeInfiniteQueryOptions< - TQueryFnData, - TError, - TData, - TQueryKey, - TPageParam -> & - OptionalInitialData - export type ManualUndefinedInitialDataInfiniteOptions< TQueryFnData, TError = DefaultError, @@ -83,14 +67,14 @@ export type ManualUndefinedInitialDataInfiniteOptions< > & OptionalInitialData -export type UnusedSkipTokenDeclarativeInfiniteOptions< +export type UnusedSkipTokenManualInfiniteOptions< TQueryFnData, TError = DefaultError, TData = InfiniteData, TQueryKey extends QueryKey = QueryKey, TPageParam = unknown, > = WithoutSkipTokenQueryFn< - CreateDeclarativeInfiniteQueryOptions< + CreateManualInfiniteQueryOptions< TQueryFnData, TError, TData, @@ -99,59 +83,24 @@ export type UnusedSkipTokenDeclarativeInfiniteOptions< > > -export type UnusedSkipTokenManualInfiniteOptions< +export type UnusedSkipTokenInfiniteOptions< TQueryFnData, TError = DefaultError, TData = InfiniteData, TQueryKey extends QueryKey = QueryKey, TPageParam = unknown, + TMode extends InfiniteQueryMode | undefined = InfiniteQueryMode | undefined, > = WithoutSkipTokenQueryFn< - CreateManualInfiniteQueryOptions< + CreateInfiniteQueryOptions< TQueryFnData, TError, TData, TQueryKey, - TPageParam + TPageParam, + TMode > > -export type UnusedSkipTokenInfiniteOptions< - TQueryFnData, - TError = DefaultError, - TData = InfiniteData, - TQueryKey extends QueryKey = QueryKey, - TPageParam = unknown, -> = - | UnusedSkipTokenDeclarativeInfiniteOptions< - TQueryFnData, - TError, - TData, - TQueryKey, - TPageParam - > - | UnusedSkipTokenManualInfiniteOptions< - TQueryFnData, - TError, - TData, - TQueryKey, - TPageParam - > - -export type DeclarativeDefinedInitialDataInfiniteOptions< - TQueryFnData, - TError = DefaultError, - TData = InfiniteData, - TQueryKey extends QueryKey = QueryKey, - TPageParam = unknown, -> = CreateDeclarativeInfiniteQueryOptions< - TQueryFnData, - TError, - TData, - TQueryKey, - TPageParam -> & - RequiredInitialData - export type ManualDefinedInitialDataInfiniteOptions< TQueryFnData, TError = DefaultError, @@ -191,19 +140,21 @@ export function infiniteQueryOptions< TQueryKey extends QueryKey = QueryKey, TPageParam = unknown, >( - options: DeclarativeDefinedInitialDataInfiniteOptions< + options: DefinedInitialDataInfiniteOptions< TQueryFnData, TError, TData, TQueryKey, - TPageParam + TPageParam, + undefined >, -): DeclarativeDefinedInitialDataInfiniteOptions< +): DefinedInitialDataInfiniteOptions< TQueryFnData, TError, TData, TQueryKey, - TPageParam + TPageParam, + undefined > & { queryKey: DataTag, TError> } @@ -237,19 +188,21 @@ export function infiniteQueryOptions< TQueryKey extends QueryKey = QueryKey, TPageParam = unknown, >( - options: UnusedSkipTokenDeclarativeInfiniteOptions< + options: UnusedSkipTokenInfiniteOptions< TQueryFnData, TError, TData, TQueryKey, - TPageParam + TPageParam, + undefined >, -): UnusedSkipTokenDeclarativeInfiniteOptions< +): UnusedSkipTokenInfiniteOptions< TQueryFnData, TError, TData, TQueryKey, - TPageParam + TPageParam, + undefined > & { queryKey: DataTag, TError> } @@ -283,19 +236,21 @@ export function infiniteQueryOptions< TQueryKey extends QueryKey = QueryKey, TPageParam = unknown, >( - options: DeclarativeUndefinedInitialDataInfiniteOptions< + options: UndefinedInitialDataInfiniteOptions< TQueryFnData, TError, TData, TQueryKey, - TPageParam + TPageParam, + undefined >, -): DeclarativeUndefinedInitialDataInfiniteOptions< +): UndefinedInitialDataInfiniteOptions< TQueryFnData, TError, TData, TQueryKey, - TPageParam + TPageParam, + undefined > & { queryKey: DataTag, TError> } diff --git a/packages/angular-query-experimental/src/inject-infinite-query.ts b/packages/angular-query-experimental/src/inject-infinite-query.ts index 3ba7da03360..c1aec6f32c6 100644 --- a/packages/angular-query-experimental/src/inject-infinite-query.ts +++ b/packages/angular-query-experimental/src/inject-infinite-query.ts @@ -11,7 +11,6 @@ import type { InfiniteData, InfiniteQueryMode, QueryKey, - QueryObserver, } from '@tanstack/query-core' import type { CreateInfiniteQueryOptions, @@ -20,8 +19,6 @@ import type { } from './types' import type { DefinedInitialDataInfiniteOptions, - DeclarativeDefinedInitialDataInfiniteOptions, - DeclarativeUndefinedInitialDataInfiniteOptions, ManualDefinedInitialDataInfiniteOptions, ManualUndefinedInitialDataInfiniteOptions, UndefinedInitialDataInfiniteOptions, @@ -144,12 +141,13 @@ export function injectInfiniteQuery< TQueryKey extends QueryKey = QueryKey, TPageParam = unknown, >( - injectInfiniteQueryFn: () => DeclarativeDefinedInitialDataInfiniteOptions< + injectInfiniteQueryFn: () => DefinedInitialDataInfiniteOptions< TQueryFnData, TError, TData, TQueryKey, - TPageParam + TPageParam, + undefined >, options?: InjectInfiniteQueryOptions, ): DefinedCreateInfiniteQueryResult @@ -256,12 +254,13 @@ export function injectInfiniteQuery< TQueryKey extends QueryKey = QueryKey, TPageParam = unknown, >( - injectInfiniteQueryFn: () => DeclarativeUndefinedInitialDataInfiniteOptions< + injectInfiniteQueryFn: () => UndefinedInitialDataInfiniteOptions< TQueryFnData, TError, TData, TQueryKey, - TPageParam + TPageParam, + undefined >, options?: InjectInfiniteQueryOptions, ): CreateInfiniteQueryResult @@ -444,13 +443,10 @@ export function injectInfiniteQuery< export function injectInfiniteQuery( injectInfiniteQueryFn: () => any, options?: InjectInfiniteQueryOptions, -): any { +) { !options?.injector && assertInInjectionContext(injectInfiniteQuery) return runInInjectionContext(options?.injector ?? inject(Injector), () => - createBaseQuery( - injectInfiniteQueryFn as unknown as () => any, - InfiniteQueryObserver as typeof QueryObserver, - ), - ) as any + createBaseQuery(injectInfiniteQueryFn, InfiniteQueryObserver), + ) } diff --git a/packages/angular-query-experimental/src/types.ts b/packages/angular-query-experimental/src/types.ts index 92def8164fe..eaf583ba217 100644 --- a/packages/angular-query-experimental/src/types.ts +++ b/packages/angular-query-experimental/src/types.ts @@ -89,32 +89,18 @@ export type CreateInfiniteQueryOptions< TQueryKey, TPageParam > - : CreateDeclarativeInfiniteQueryOptions< - TQueryFnData, - TError, - TData, - TQueryKey, - TPageParam + : DistributiveOmit< + InfiniteQueryObserverOptions< + TQueryFnData, + TError, + TData, + TQueryKey, + TPageParam, + undefined + >, + 'suspense' > -export type CreateDeclarativeInfiniteQueryOptions< - TQueryFnData = unknown, - TError = DefaultError, - TData = TQueryFnData, - TQueryKey extends QueryKey = QueryKey, - TPageParam = unknown, -> = DistributiveOmit< - InfiniteQueryObserverOptions< - TQueryFnData, - TError, - TData, - TQueryKey, - TPageParam, - undefined - >, - 'suspense' -> - export type CreateManualInfiniteQueryOptions< TQueryFnData = unknown, TError = DefaultError, diff --git a/packages/react-query/src/infiniteQueryOptions.ts b/packages/react-query/src/infiniteQueryOptions.ts index 350ddb572fd..df7fab28ad2 100644 --- a/packages/react-query/src/infiniteQueryOptions.ts +++ b/packages/react-query/src/infiniteQueryOptions.ts @@ -2,6 +2,7 @@ import type { DataTag, DefaultError, InfiniteData, + InfiniteQueryMode, InitialDataFunction, NonUndefinedGuard, OmitKeyof, @@ -9,9 +10,8 @@ import type { SkipToken, } from '@tanstack/query-core' import type { - UseDeclarativeInfiniteQueryOptions, - UseManualInfiniteQueryOptions, UseInfiniteQueryOptions, + UseInfiniteQueryOptionsBase, } from './types' type OptionalInitialData = { @@ -50,27 +50,14 @@ export type UndefinedInitialDataInfiniteOptions< TData = InfiniteData, TQueryKey extends QueryKey = QueryKey, TPageParam = unknown, + TMode extends InfiniteQueryMode | undefined = InfiniteQueryMode | undefined, > = UseInfiniteQueryOptions< TQueryFnData, TError, TData, TQueryKey, - TPageParam -> & - OptionalInitialData - -export type DeclarativeUndefinedInitialDataInfiniteOptions< - TQueryFnData, - TError = DefaultError, - TData = InfiniteData, - TQueryKey extends QueryKey = QueryKey, - TPageParam = unknown, -> = UseDeclarativeInfiniteQueryOptions< - TQueryFnData, - TError, - TData, - TQueryKey, - TPageParam + TPageParam, + TMode > & OptionalInitialData @@ -80,103 +67,65 @@ export type ManualUndefinedInitialDataInfiniteOptions< TData = InfiniteData, TQueryKey extends QueryKey = QueryKey, TPageParam = unknown, -> = UseManualInfiniteQueryOptions< +> = UseInfiniteQueryOptionsBase< TQueryFnData, TError, TData, TQueryKey, - TPageParam + TPageParam, + InfiniteQueryMode > & OptionalInitialData -export type UnusedSkipTokenDeclarativeInfiniteOptions< +export type UnusedSkipTokenManualInfiniteOptions< TQueryFnData, TError = DefaultError, TData = InfiniteData, TQueryKey extends QueryKey = QueryKey, TPageParam = unknown, > = WithoutSkipTokenQueryFn< - UseDeclarativeInfiniteQueryOptions< + UseInfiniteQueryOptionsBase< TQueryFnData, TError, TData, TQueryKey, - TPageParam + TPageParam, + InfiniteQueryMode > > -export type UnusedSkipTokenManualInfiniteOptions< +export type UnusedSkipTokenInfiniteOptions< TQueryFnData, TError = DefaultError, TData = InfiniteData, TQueryKey extends QueryKey = QueryKey, TPageParam = unknown, + TMode extends InfiniteQueryMode | undefined = InfiniteQueryMode | undefined, > = WithoutSkipTokenQueryFn< - UseManualInfiniteQueryOptions< + UseInfiniteQueryOptions< TQueryFnData, TError, TData, TQueryKey, - TPageParam + TPageParam, + TMode > > -export type UnusedSkipTokenInfiniteOptions< - TQueryFnData, - TError = DefaultError, - TData = InfiniteData, - TQueryKey extends QueryKey = QueryKey, - TPageParam = unknown, -> = - | UnusedSkipTokenDeclarativeInfiniteOptions< - TQueryFnData, - TError, - TData, - TQueryKey, - TPageParam - > - | UnusedSkipTokenManualInfiniteOptions< - TQueryFnData, - TError, - TData, - TQueryKey, - TPageParam - > - export type DefinedInitialDataInfiniteOptions< TQueryFnData, TError = DefaultError, TData = InfiniteData, TQueryKey extends QueryKey = QueryKey, TPageParam = unknown, -> = - | DeclarativeDefinedInitialDataInfiniteOptions< - TQueryFnData, - TError, - TData, - TQueryKey, - TPageParam - > - | ManualDefinedInitialDataInfiniteOptions< - TQueryFnData, - TError, - TData, - TQueryKey, - TPageParam - > - -export type DeclarativeDefinedInitialDataInfiniteOptions< - TQueryFnData, - TError = DefaultError, - TData = InfiniteData, - TQueryKey extends QueryKey = QueryKey, - TPageParam = unknown, -> = UseDeclarativeInfiniteQueryOptions< + TMode extends InfiniteQueryMode | undefined = InfiniteQueryMode | undefined, +> = UseInfiniteQueryOptions< TQueryFnData, TError, TData, TQueryKey, - TPageParam + TPageParam, + TMode > & RequiredInitialData @@ -186,12 +135,13 @@ export type ManualDefinedInitialDataInfiniteOptions< TData = InfiniteData, TQueryKey extends QueryKey = QueryKey, TPageParam = unknown, -> = UseManualInfiniteQueryOptions< +> = UseInfiniteQueryOptionsBase< TQueryFnData, TError, TData, TQueryKey, - TPageParam + TPageParam, + InfiniteQueryMode > & RequiredInitialData @@ -202,19 +152,21 @@ export function infiniteQueryOptions< TQueryKey extends QueryKey = QueryKey, TPageParam = unknown, >( - options: DeclarativeDefinedInitialDataInfiniteOptions< + options: DefinedInitialDataInfiniteOptions< TQueryFnData, TError, TData, TQueryKey, - TPageParam + TPageParam, + undefined >, -): DeclarativeDefinedInitialDataInfiniteOptions< +): DefinedInitialDataInfiniteOptions< TQueryFnData, TError, TData, TQueryKey, - TPageParam + TPageParam, + undefined > & TaggedInfiniteQueryOptions<{}, TQueryKey, TQueryFnData, TError> @@ -248,19 +200,21 @@ export function infiniteQueryOptions< TQueryKey extends QueryKey = QueryKey, TPageParam = unknown, >( - options: UnusedSkipTokenDeclarativeInfiniteOptions< + options: UnusedSkipTokenInfiniteOptions< TQueryFnData, TError, TData, TQueryKey, - TPageParam + TPageParam, + undefined >, -): UnusedSkipTokenDeclarativeInfiniteOptions< +): UnusedSkipTokenInfiniteOptions< TQueryFnData, TError, TData, TQueryKey, - TPageParam + TPageParam, + undefined > & TaggedInfiniteQueryOptions<{}, TQueryKey, TQueryFnData, TError> @@ -294,19 +248,21 @@ export function infiniteQueryOptions< TQueryKey extends QueryKey = QueryKey, TPageParam = unknown, >( - options: DeclarativeUndefinedInitialDataInfiniteOptions< + options: UndefinedInitialDataInfiniteOptions< TQueryFnData, TError, TData, TQueryKey, - TPageParam + TPageParam, + undefined >, -): DeclarativeUndefinedInitialDataInfiniteOptions< +): UndefinedInitialDataInfiniteOptions< TQueryFnData, TError, TData, TQueryKey, - TPageParam + TPageParam, + undefined > & TaggedInfiniteQueryOptions<{}, TQueryKey, TQueryFnData, TError> diff --git a/packages/react-query/src/types.ts b/packages/react-query/src/types.ts index 2adaa1fb9e9..180e2af6e25 100644 --- a/packages/react-query/src/types.ts +++ b/packages/react-query/src/types.ts @@ -99,9 +99,10 @@ export type AnyUseInfiniteQueryOptions = UseInfiniteQueryOptions< any, any, any, + any, any > -type UseInfiniteQueryOptionsBase< +export type UseInfiniteQueryOptionsBase< TQueryFnData = unknown, TError = DefaultError, TData = TQueryFnData, @@ -122,58 +123,22 @@ type UseInfiniteQueryOptionsBase< subscribed?: boolean } -export type UseDeclarativeInfiniteQueryOptions< - TQueryFnData = unknown, - TError = DefaultError, - TData = TQueryFnData, - TQueryKey extends QueryKey = QueryKey, - TPageParam = unknown, -> = UseInfiniteQueryOptionsBase< - TQueryFnData, - TError, - TData, - TQueryKey, - TPageParam, - undefined -> - -export type UseManualInfiniteQueryOptions< +export type UseInfiniteQueryOptions< TQueryFnData = unknown, TError = DefaultError, TData = TQueryFnData, TQueryKey extends QueryKey = QueryKey, TPageParam = unknown, + TMode extends InfiniteQueryMode | undefined = InfiniteQueryMode | undefined, > = UseInfiniteQueryOptionsBase< TQueryFnData, TError, TData, TQueryKey, TPageParam, - InfiniteQueryMode + TMode > -export type UseInfiniteQueryOptions< - TQueryFnData = unknown, - TError = DefaultError, - TData = TQueryFnData, - TQueryKey extends QueryKey = QueryKey, - TPageParam = unknown, -> = - | UseDeclarativeInfiniteQueryOptions< - TQueryFnData, - TError, - TData, - TQueryKey, - TPageParam - > - | UseManualInfiniteQueryOptions< - TQueryFnData, - TError, - TData, - TQueryKey, - TPageParam - > - export type UseSuspenseInfiniteQueryOptions< TQueryFnData = unknown, TError = DefaultError, @@ -182,12 +147,13 @@ export type UseSuspenseInfiniteQueryOptions< TPageParam = unknown, > = | (DistributiveOmit< - UseDeclarativeInfiniteQueryOptions< + UseInfiniteQueryOptions< TQueryFnData, TError, TData, TQueryKey, - TPageParam + TPageParam, + undefined >, 'queryFn' | 'enabled' | 'throwOnError' | 'placeholderData' > & { @@ -196,23 +162,25 @@ export type UseSuspenseInfiniteQueryOptions< * Defaults to `true`. */ queryFn?: Exclude< - UseDeclarativeInfiniteQueryOptions< + UseInfiniteQueryOptions< TQueryFnData, TError, TData, TQueryKey, - TPageParam + TPageParam, + undefined >['queryFn'], SkipToken > }) | (DistributiveOmit< - UseManualInfiniteQueryOptions< + UseInfiniteQueryOptionsBase< TQueryFnData, TError, TData, TQueryKey, - TPageParam + TPageParam, + InfiniteQueryMode >, 'queryFn' | 'enabled' | 'throwOnError' | 'placeholderData' > & { @@ -221,12 +189,13 @@ export type UseSuspenseInfiniteQueryOptions< * Defaults to `true`. */ queryFn?: Exclude< - UseManualInfiniteQueryOptions< + UseInfiniteQueryOptionsBase< TQueryFnData, TError, TData, TQueryKey, - TPageParam + TPageParam, + InfiniteQueryMode >['queryFn'], SkipToken > diff --git a/packages/react-query/src/useInfiniteQuery.ts b/packages/react-query/src/useInfiniteQuery.ts index 4571c280409..44dfa296b5d 100644 --- a/packages/react-query/src/useInfiniteQuery.ts +++ b/packages/react-query/src/useInfiniteQuery.ts @@ -15,10 +15,10 @@ import type { UseInfiniteQueryResult, } from './types' import type { - DeclarativeDefinedInitialDataInfiniteOptions, - DeclarativeUndefinedInitialDataInfiniteOptions, + DefinedInitialDataInfiniteOptions, ManualDefinedInitialDataInfiniteOptions, ManualUndefinedInitialDataInfiniteOptions, + UndefinedInitialDataInfiniteOptions, } from './infiniteQueryOptions' export function useInfiniteQuery< @@ -28,12 +28,13 @@ export function useInfiniteQuery< TQueryKey extends QueryKey = QueryKey, TPageParam = unknown, >( - options: DeclarativeDefinedInitialDataInfiniteOptions< + options: DefinedInitialDataInfiniteOptions< TQueryFnData, TError, TData, TQueryKey, - TPageParam + TPageParam, + undefined >, queryClient?: QueryClient, ): DefinedUseInfiniteQueryResult @@ -62,12 +63,13 @@ export function useInfiniteQuery< TQueryKey extends QueryKey = QueryKey, TPageParam = unknown, >( - options: DeclarativeUndefinedInitialDataInfiniteOptions< + options: UndefinedInitialDataInfiniteOptions< TQueryFnData, TError, TData, TQueryKey, - TPageParam + TPageParam, + undefined >, queryClient?: QueryClient, ): UseInfiniteQueryResult From e3746eb29344c487601b5920a0b31048a9fda676 Mon Sep 17 00:00:00 2001 From: TkDodo Date: Fri, 3 Apr 2026 20:21:19 +0200 Subject: [PATCH 19/25] removing additional check that is covered by calling getPreviousPageParam directly --- packages/query-core/src/infiniteQueryBehavior.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/query-core/src/infiniteQueryBehavior.ts b/packages/query-core/src/infiniteQueryBehavior.ts index 5b1188aff2d..8dc807f5d17 100644 --- a/packages/query-core/src/infiniteQueryBehavior.ts +++ b/packages/query-core/src/infiniteQueryBehavior.ts @@ -178,7 +178,7 @@ export function hasPreviousPage( options: InfiniteQueryPageParamsOptions, data?: InfiniteData, ): boolean { - if (!data || !options.getPreviousPageParam) { + if (!data) { return false } return getPreviousPageParam(options, data) != null From 730cc0871cf79eb271e35e7f3ff9d5620797cfb0 Mon Sep 17 00:00:00 2001 From: TkDodo Date: Fri, 3 Apr 2026 20:21:49 +0200 Subject: [PATCH 20/25] simplify types some more --- .../src/infinite-query-options.ts | 17 ++++++---- .../angular-query-experimental/src/types.ts | 32 +++++++------------ 2 files changed, 21 insertions(+), 28 deletions(-) diff --git a/packages/angular-query-experimental/src/infinite-query-options.ts b/packages/angular-query-experimental/src/infinite-query-options.ts index e1b821b34c8..0e766d1f5b7 100644 --- a/packages/angular-query-experimental/src/infinite-query-options.ts +++ b/packages/angular-query-experimental/src/infinite-query-options.ts @@ -11,7 +11,7 @@ import type { } from '@tanstack/query-core' import type { CreateInfiniteQueryOptions, - CreateManualInfiniteQueryOptions, + CreateInfiniteQueryOptionsBase, } from './types' type OptionalInitialData = { @@ -58,12 +58,13 @@ export type ManualUndefinedInitialDataInfiniteOptions< TData = InfiniteData, TQueryKey extends QueryKey = QueryKey, TPageParam = unknown, -> = CreateManualInfiniteQueryOptions< +> = CreateInfiniteQueryOptionsBase< TQueryFnData, TError, TData, TQueryKey, - TPageParam + TPageParam, + InfiniteQueryMode > & OptionalInitialData @@ -74,12 +75,13 @@ export type UnusedSkipTokenManualInfiniteOptions< TQueryKey extends QueryKey = QueryKey, TPageParam = unknown, > = WithoutSkipTokenQueryFn< - CreateManualInfiniteQueryOptions< + CreateInfiniteQueryOptionsBase< TQueryFnData, TError, TData, TQueryKey, - TPageParam + TPageParam, + InfiniteQueryMode > > @@ -107,12 +109,13 @@ export type ManualDefinedInitialDataInfiniteOptions< TData = InfiniteData, TQueryKey extends QueryKey = QueryKey, TPageParam = unknown, -> = CreateManualInfiniteQueryOptions< +> = CreateInfiniteQueryOptionsBase< TQueryFnData, TError, TData, TQueryKey, - TPageParam + TPageParam, + InfiniteQueryMode > & RequiredInitialData diff --git a/packages/angular-query-experimental/src/types.ts b/packages/angular-query-experimental/src/types.ts index eaf583ba217..f7eef6ad5f2 100644 --- a/packages/angular-query-experimental/src/types.ts +++ b/packages/angular-query-experimental/src/types.ts @@ -81,32 +81,22 @@ export type CreateInfiniteQueryOptions< TQueryKey extends QueryKey = QueryKey, TPageParam = unknown, TMode extends InfiniteQueryMode | undefined = InfiniteQueryMode | undefined, -> = TMode extends InfiniteQueryMode - ? CreateManualInfiniteQueryOptions< - TQueryFnData, - TError, - TData, - TQueryKey, - TPageParam - > - : DistributiveOmit< - InfiniteQueryObserverOptions< - TQueryFnData, - TError, - TData, - TQueryKey, - TPageParam, - undefined - >, - 'suspense' - > +> = CreateInfiniteQueryOptionsBase< + TQueryFnData, + TError, + TData, + TQueryKey, + TPageParam, + TMode +> -export type CreateManualInfiniteQueryOptions< +export type CreateInfiniteQueryOptionsBase< TQueryFnData = unknown, TError = DefaultError, TData = TQueryFnData, TQueryKey extends QueryKey = QueryKey, TPageParam = unknown, + TMode extends InfiniteQueryMode | undefined = undefined, > = DistributiveOmit< InfiniteQueryObserverOptions< TQueryFnData, @@ -114,7 +104,7 @@ export type CreateManualInfiniteQueryOptions< TData, TQueryKey, TPageParam, - InfiniteQueryMode + TMode >, 'suspense' > From 8cc8498ed95e1d33cd57b723355d6be2d0101a94 Mon Sep 17 00:00:00 2001 From: TkDodo Date: Fri, 3 Apr 2026 20:23:07 +0200 Subject: [PATCH 21/25] ref: remove constructor overloads --- .../query-core/src/infiniteQueryObserver.ts | 23 ------------------- 1 file changed, 23 deletions(-) diff --git a/packages/query-core/src/infiniteQueryObserver.ts b/packages/query-core/src/infiniteQueryObserver.ts index 3d5f56ef2f9..c9f7aaee071 100644 --- a/packages/query-core/src/infiniteQueryObserver.ts +++ b/packages/query-core/src/infiniteQueryObserver.ts @@ -12,7 +12,6 @@ import type { InfiniteData, InfiniteQueryFetchNextPageArgs, InfiniteQueryFetchPreviousPageArgs, - InfiniteQueryMode, InfiniteQueryObserverBaseResult, InfiniteQueryObserverOptions, InfiniteQueryObserverResult, @@ -74,28 +73,6 @@ export class InfiniteQueryObserver< Promise> > - constructor( - client: QueryClient, - options: InfiniteQueryObserverOptions< - TQueryFnData, - TError, - TData, - TQueryKey, - TPageParam, - undefined - >, - ) - constructor( - client: QueryClient, - options: InfiniteQueryObserverOptions< - TQueryFnData, - TError, - TData, - TQueryKey, - TPageParam, - InfiniteQueryMode - >, - ) constructor( client: QueryClient, options: InfiniteQueryObserverOptions< From 5c030c8a08592d8471b8e2bf0e5bcd30d57dbd77 Mon Sep 17 00:00:00 2001 From: TkDodo Date: Fri, 3 Apr 2026 20:33:35 +0200 Subject: [PATCH 22/25] simplify some more --- .../query-core/src/infiniteQueryObserver.ts | 75 ++----------------- 1 file changed, 8 insertions(+), 67 deletions(-) diff --git a/packages/query-core/src/infiniteQueryObserver.ts b/packages/query-core/src/infiniteQueryObserver.ts index c9f7aaee071..d31106eddcd 100644 --- a/packages/query-core/src/infiniteQueryObserver.ts +++ b/packages/query-core/src/infiniteQueryObserver.ts @@ -12,7 +12,6 @@ import type { InfiniteData, InfiniteQueryFetchNextPageArgs, InfiniteQueryFetchPreviousPageArgs, - InfiniteQueryObserverBaseResult, InfiniteQueryObserverOptions, InfiniteQueryObserverResult, QueryKey, @@ -103,16 +102,9 @@ export class InfiniteQueryObserver< >, ): void { super.setOptions({ - ...(options as unknown as InfiniteQueryObserverOptions< - TQueryFnData, - TError, - TData, - TQueryKey, - TPageParam, - TMode - >), + ...options, behavior: infiniteQueryBehavior(), - } as any) + }) } getOptimisticResult( @@ -135,12 +127,8 @@ export class InfiniteQueryObserver< fetchNextPage( ...args: InfiniteQueryFetchNextPageArgs - ): Promise> - fetchNextPage( - ...args: Array ): Promise> { - const firstArg = args[0] ?? {} - const { pageParam, ...options } = firstArg + const { pageParam, ...options } = args[0] ?? ({} as any) return this.fetch({ ...options, meta: { @@ -151,12 +139,8 @@ export class InfiniteQueryObserver< fetchPreviousPage( ...args: InfiniteQueryFetchPreviousPageArgs - ): Promise> - fetchPreviousPage( - ...args: Array ): Promise> { - const firstArg = args[0] ?? {} - const { pageParam, ...options } = firstArg + const { pageParam, ...options } = args[0] ?? ({} as any) return this.fetch({ ...options, meta: { @@ -179,47 +163,9 @@ export class InfiniteQueryObserver< InfiniteData, TQueryKey >, - ): InfiniteQueryObserverResult { - return this.createInfiniteResult( - query, - options as unknown as InfiniteQueryObserverOptions< - TQueryFnData, - TError, - TData, - TQueryKey, - TPageParam, - TMode - >, - ) - } - - private createInfiniteResult( - query: Query< - TQueryFnData, - TError, - InfiniteData, - TQueryKey - >, - options: InfiniteQueryObserverOptions< - TQueryFnData, - TError, - TData, - TQueryKey, - TPageParam, - TMode - >, ): InfiniteQueryObserverResult { const { state } = query - const parentResult = super.createResult( - query, - options as QueryObserverOptions< - TQueryFnData, - TError, - TData, - InfiniteData, - TQueryKey - >, - ) + const parentResult = super.createResult(query, options) const { isFetching, isRefetching, isError, isRefetchError } = parentResult const fetchDirection = state.fetchMeta?.fetchMore?.direction @@ -230,17 +176,12 @@ export class InfiniteQueryObserver< const isFetchPreviousPageError = isError && fetchDirection === 'backward' const isFetchingPreviousPage = isFetching && fetchDirection === 'backward' - const result: InfiniteQueryObserverBaseResult< - TData, - TError, - TPageParam, - TMode - > = { + const result = { ...parentResult, fetchNextPage: this.fetchNextPage, fetchPreviousPage: this.fetchPreviousPage, - hasNextPage: hasNextPage(options, state.data), - hasPreviousPage: hasPreviousPage(options, state.data), + hasNextPage: hasNextPage(options as any, state.data), + hasPreviousPage: hasPreviousPage(options as any, state.data), isFetchNextPageError, isFetchingNextPage, isFetchPreviousPageError, From fd27a863b5610e2ed910315c519e6e59d9447339 Mon Sep 17 00:00:00 2001 From: TkDodo Date: Fri, 3 Apr 2026 22:37:35 +0200 Subject: [PATCH 23/25] switch to a union type --- .../query-core/src/infiniteQueryObserver.ts | 24 +++ packages/query-core/src/queryClient.ts | 109 +------------- packages/query-core/src/types.ts | 137 +++++++++++++++--- packages/vue-query/src/queryClient.ts | 15 +- packages/vue-query/src/useInfiniteQuery.ts | 8 +- 5 files changed, 156 insertions(+), 137 deletions(-) diff --git a/packages/query-core/src/infiniteQueryObserver.ts b/packages/query-core/src/infiniteQueryObserver.ts index d31106eddcd..d14285758ef 100644 --- a/packages/query-core/src/infiniteQueryObserver.ts +++ b/packages/query-core/src/infiniteQueryObserver.ts @@ -12,6 +12,8 @@ import type { InfiniteData, InfiniteQueryFetchNextPageArgs, InfiniteQueryFetchPreviousPageArgs, + InfiniteQueryMode, + InfiniteQueryObserverOptionsBase, InfiniteQueryObserverOptions, InfiniteQueryObserverResult, QueryKey, @@ -75,6 +77,28 @@ export class InfiniteQueryObserver< constructor( client: QueryClient, options: InfiniteQueryObserverOptions< + TQueryFnData, + TError, + TData, + TQueryKey, + TPageParam, + undefined + >, + ) + constructor( + client: QueryClient, + options: InfiniteQueryObserverOptions< + TQueryFnData, + TError, + TData, + TQueryKey, + TPageParam, + InfiniteQueryMode + >, + ) + constructor( + client: QueryClient, + options: InfiniteQueryObserverOptionsBase< TQueryFnData, TError, TData, diff --git a/packages/query-core/src/queryClient.ts b/packages/query-core/src/queryClient.ts index 1708678abe5..e3b22034b70 100644 --- a/packages/query-core/src/queryClient.ts +++ b/packages/query-core/src/queryClient.ts @@ -20,7 +20,6 @@ import type { DefaultedQueryObserverOptions, EnsureInfiniteQueryDataOptions, EnsureQueryDataOptions, - FetchPageDirectionMode, FetchInfiniteQueryOptions, FetchQueryOptions, InferDataFromTag, @@ -393,41 +392,7 @@ export class QueryClient { TError, TData, TQueryKey, - TPageParam, - undefined - >, - ): Promise> - fetchInfiniteQuery< - TQueryFnData, - TError = DefaultError, - TData = TQueryFnData, - TQueryKey extends QueryKey = QueryKey, - TPageParam = unknown, - >( - options: FetchInfiniteQueryOptions< - TQueryFnData, - TError, - TData, - TQueryKey, - TPageParam, - 'manual' - >, - ): Promise> - fetchInfiniteQuery< - TQueryFnData, - TError = DefaultError, - TData = TQueryFnData, - TQueryKey extends QueryKey = QueryKey, - TPageParam = unknown, - TMode extends FetchPageDirectionMode = undefined, - >( - options: FetchInfiniteQueryOptions< - TQueryFnData, - TError, - TData, - TQueryKey, - TPageParam, - TMode + TPageParam >, ): Promise> { options.behavior = infiniteQueryBehavior< @@ -451,41 +416,7 @@ export class QueryClient { TError, TData, TQueryKey, - TPageParam, - undefined - >, - ): Promise - prefetchInfiniteQuery< - TQueryFnData, - TError = DefaultError, - TData = TQueryFnData, - TQueryKey extends QueryKey = QueryKey, - TPageParam = unknown, - >( - options: FetchInfiniteQueryOptions< - TQueryFnData, - TError, - TData, - TQueryKey, - TPageParam, - 'manual' - >, - ): Promise - prefetchInfiniteQuery< - TQueryFnData, - TError = DefaultError, - TData = TQueryFnData, - TQueryKey extends QueryKey = QueryKey, - TPageParam = unknown, - TMode extends FetchPageDirectionMode = undefined, - >( - options: FetchInfiniteQueryOptions< - TQueryFnData, - TError, - TData, - TQueryKey, - TPageParam, - TMode + TPageParam >, ): Promise { return this.fetchInfiniteQuery(options as any) @@ -505,41 +436,7 @@ export class QueryClient { TError, TData, TQueryKey, - TPageParam, - undefined - >, - ): Promise> - ensureInfiniteQueryData< - TQueryFnData, - TError = DefaultError, - TData = TQueryFnData, - TQueryKey extends QueryKey = QueryKey, - TPageParam = unknown, - >( - options: EnsureInfiniteQueryDataOptions< - TQueryFnData, - TError, - TData, - TQueryKey, - TPageParam, - 'manual' - >, - ): Promise> - ensureInfiniteQueryData< - TQueryFnData, - TError = DefaultError, - TData = TQueryFnData, - TQueryKey extends QueryKey = QueryKey, - TPageParam = unknown, - TMode extends FetchPageDirectionMode = undefined, - >( - options: EnsureInfiniteQueryDataOptions< - TQueryFnData, - TError, - TData, - TQueryKey, - TPageParam, - TMode + TPageParam >, ): Promise> { options.behavior = infiniteQueryBehavior< diff --git a/packages/query-core/src/types.ts b/packages/query-core/src/types.ts index b451b6e1553..32552343d7d 100644 --- a/packages/query-core/src/types.ts +++ b/packages/query-core/src/types.ts @@ -311,10 +311,12 @@ export interface InfiniteQueryPageParamsManualOptions< export type InfiniteQueryPageParamsOptions< TQueryFnData = unknown, TPageParam = unknown, - TMode extends InfiniteQueryMode | undefined = undefined, -> = TMode extends InfiniteQueryMode - ? InfiniteQueryPageParamsManualOptions - : InfiniteQueryPageParamsDeclarativeOptions + TMode extends FetchPageDirectionMode = FetchPageDirectionMode, +> = TMode extends FetchPageDirectionMode + ? TMode extends InfiniteQueryMode + ? InfiniteQueryPageParamsManualOptions + : InfiniteQueryPageParamsDeclarativeOptions + : never export type FetchPageDirectionMode = InfiniteQueryMode | undefined @@ -483,7 +485,7 @@ export type DefaultedQueryObserverOptions< 'throwOnError' | 'refetchOnReconnect' | 'queryHash' > -export type InfiniteQueryObserverOptions< +export type InfiniteQueryObserverOptionsBase< TQueryFnData = unknown, TError = DefaultError, TData = TQueryFnData, @@ -500,7 +502,25 @@ export type InfiniteQueryObserverOptions< > & InfiniteQueryPageParamsOptions -export type DefaultedInfiniteQueryObserverOptions< +export type InfiniteQueryObserverOptions< + TQueryFnData = unknown, + TError = DefaultError, + TData = TQueryFnData, + TQueryKey extends QueryKey = QueryKey, + TPageParam = unknown, + TMode extends FetchPageDirectionMode = FetchPageDirectionMode, +> = TMode extends FetchPageDirectionMode + ? InfiniteQueryObserverOptionsBase< + TQueryFnData, + TError, + TData, + TQueryKey, + TPageParam, + TMode + > + : never + +export type DefaultedInfiniteQueryObserverOptionsBase< TQueryFnData = unknown, TError = DefaultError, TData = TQueryFnData, @@ -508,7 +528,7 @@ export type DefaultedInfiniteQueryObserverOptions< TPageParam = unknown, TMode extends FetchPageDirectionMode = undefined, > = WithRequired< - InfiniteQueryObserverOptions< + InfiniteQueryObserverOptionsBase< TQueryFnData, TError, TData, @@ -519,6 +539,24 @@ export type DefaultedInfiniteQueryObserverOptions< 'throwOnError' | 'refetchOnReconnect' | 'queryHash' > +export type DefaultedInfiniteQueryObserverOptions< + TQueryFnData = unknown, + TError = DefaultError, + TData = TQueryFnData, + TQueryKey extends QueryKey = QueryKey, + TPageParam = unknown, + TMode extends FetchPageDirectionMode = FetchPageDirectionMode, +> = TMode extends FetchPageDirectionMode + ? DefaultedInfiniteQueryObserverOptionsBase< + TQueryFnData, + TError, + TData, + TQueryKey, + TPageParam, + TMode + > + : never + export interface FetchQueryOptions< TQueryFnData = unknown, TError = DefaultError, @@ -553,14 +591,14 @@ export interface EnsureQueryDataOptions< revalidateIfStale?: boolean } -export type EnsureInfiniteQueryDataOptions< +export type EnsureInfiniteQueryDataOptionsBase< TQueryFnData = unknown, TError = DefaultError, TData = TQueryFnData, TQueryKey extends QueryKey = QueryKey, TPageParam = unknown, TMode extends FetchPageDirectionMode = undefined, -> = FetchInfiniteQueryOptions< +> = FetchInfiniteQueryOptionsBase< TQueryFnData, TError, TData, @@ -571,6 +609,24 @@ export type EnsureInfiniteQueryDataOptions< revalidateIfStale?: boolean } +export type EnsureInfiniteQueryDataOptions< + TQueryFnData = unknown, + TError = DefaultError, + TData = TQueryFnData, + TQueryKey extends QueryKey = QueryKey, + TPageParam = unknown, + TMode extends FetchPageDirectionMode = FetchPageDirectionMode, +> = TMode extends FetchPageDirectionMode + ? EnsureInfiniteQueryDataOptionsBase< + TQueryFnData, + TError, + TData, + TQueryKey, + TPageParam, + TMode + > + : never + type FetchInfiniteQueryPagesDeclarative< TQueryFnData = unknown, TPageParam = unknown, @@ -581,20 +637,41 @@ type FetchInfiniteQueryPagesDeclarative< getNextPageParam: GetNextPageParamFunction } -type FetchInfiniteQueryPages< +interface FetchInfiniteQueryPageParamsDeclarativeOptions< TQueryFnData = unknown, TPageParam = unknown, - TMode extends FetchPageDirectionMode = undefined, -> = TMode extends InfiniteQueryMode - ? { - mode: InfiniteQueryMode - pages?: never - getNextPageParam?: never - getPreviousPageParam?: never - } - : FetchInfiniteQueryPagesDeclarative +> extends InitialPageParam { + mode?: never + getPreviousPageParam?: GetPreviousPageParamFunction + getNextPageParam?: GetNextPageParamFunction +} -export type FetchInfiniteQueryOptions< +type FetchInfiniteQueryPageParamsOptions< + TQueryFnData = unknown, + TPageParam = unknown, + TMode extends FetchPageDirectionMode = FetchPageDirectionMode, +> = TMode extends FetchPageDirectionMode + ? TMode extends InfiniteQueryMode + ? InfiniteQueryPageParamsManualOptions + : FetchInfiniteQueryPageParamsDeclarativeOptions + : never + +type FetchInfiniteQueryPages< + TQueryFnData = unknown, + TPageParam = unknown, + TMode extends FetchPageDirectionMode = FetchPageDirectionMode, +> = TMode extends FetchPageDirectionMode + ? TMode extends InfiniteQueryMode + ? { + mode: InfiniteQueryMode + pages?: never + getNextPageParam?: never + getPreviousPageParam?: never + } + : FetchInfiniteQueryPagesDeclarative + : never + +export type FetchInfiniteQueryOptionsBase< TQueryFnData = unknown, TError = DefaultError, TData = TQueryFnData, @@ -611,9 +688,27 @@ export type FetchInfiniteQueryOptions< >, 'initialPageParam' > & - InfiniteQueryPageParamsOptions & + FetchInfiniteQueryPageParamsOptions & FetchInfiniteQueryPages +export type FetchInfiniteQueryOptions< + TQueryFnData = unknown, + TError = DefaultError, + TData = TQueryFnData, + TQueryKey extends QueryKey = QueryKey, + TPageParam = unknown, + TMode extends FetchPageDirectionMode = FetchPageDirectionMode, +> = TMode extends FetchPageDirectionMode + ? FetchInfiniteQueryOptionsBase< + TQueryFnData, + TError, + TData, + TQueryKey, + TPageParam, + TMode + > + : never + export interface ResultOptions { throwOnError?: boolean } diff --git a/packages/vue-query/src/queryClient.ts b/packages/vue-query/src/queryClient.ts index 3ef5ab96233..2c02d047842 100644 --- a/packages/vue-query/src/queryClient.ts +++ b/packages/vue-query/src/queryClient.ts @@ -12,6 +12,7 @@ import type { DefaultOptions, EnsureQueryDataOptions, FetchInfiniteQueryOptions, + FetchInfiniteQueryOptionsBase, FetchQueryOptions, InferDataFromTag, InferErrorFromTag, @@ -401,16 +402,17 @@ export class QueryClient extends QC { TPageParam = unknown, >( options: MaybeRefDeep< - FetchInfiniteQueryOptions< + FetchInfiniteQueryOptionsBase< TQueryFnData, TError, TData, TQueryKey, TPageParam, - InfiniteQueryMode | undefined + undefined > >, - ): Promise> { + ): Promise> + fetchInfiniteQuery(options: any): Promise { return super.fetchInfiniteQuery(cloneDeepUnref(options) as any) } @@ -490,16 +492,17 @@ export class QueryClient extends QC { TPageParam = unknown, >( options: MaybeRefDeep< - FetchInfiniteQueryOptions< + FetchInfiniteQueryOptionsBase< TQueryFnData, TError, TData, TQueryKey, TPageParam, - InfiniteQueryMode | undefined + undefined > >, - ): Promise { + ): Promise + prefetchInfiniteQuery(options: any): Promise { return super.prefetchInfiniteQuery(cloneDeepUnref(options) as any) } diff --git a/packages/vue-query/src/useInfiniteQuery.ts b/packages/vue-query/src/useInfiniteQuery.ts index 7f7056312cd..722808a32a1 100644 --- a/packages/vue-query/src/useInfiniteQuery.ts +++ b/packages/vue-query/src/useInfiniteQuery.ts @@ -8,7 +8,7 @@ import type { DefaultError, InfiniteData, InfiniteQueryMode, - InfiniteQueryObserverOptions, + InfiniteQueryObserverOptionsBase, InfiniteQueryObserverResult, QueryKey, QueryObserver, @@ -34,7 +34,7 @@ export type UseInfiniteQueryOptions< TMode extends InfiniteQueryMode | undefined = undefined, > = MaybeRef< { - [Property in keyof InfiniteQueryObserverOptions< + [Property in keyof InfiniteQueryObserverOptionsBase< TQueryFnData, TError, TData, @@ -43,7 +43,7 @@ export type UseInfiniteQueryOptions< TMode >]: Property extends 'enabled' ? MaybeRefOrGetter< - InfiniteQueryObserverOptions< + InfiniteQueryObserverOptionsBase< TQueryFnData, TError, TData, @@ -53,7 +53,7 @@ export type UseInfiniteQueryOptions< >[Property] > : MaybeRefDeep< - InfiniteQueryObserverOptions< + InfiniteQueryObserverOptionsBase< TQueryFnData, TError, TData, From 0e6571231faca8c13830ddf7f0a2dc73a6eb042c Mon Sep 17 00:00:00 2001 From: TkDodo Date: Fri, 3 Apr 2026 22:41:23 +0200 Subject: [PATCH 24/25] simplify type assertion --- .../query-core/src/infiniteQueryBehavior.ts | 19 +++++++++++++------ .../query-core/src/infiniteQueryObserver.ts | 2 +- 2 files changed, 14 insertions(+), 7 deletions(-) diff --git a/packages/query-core/src/infiniteQueryBehavior.ts b/packages/query-core/src/infiniteQueryBehavior.ts index 8dc807f5d17..d512e11cc60 100644 --- a/packages/query-core/src/infiniteQueryBehavior.ts +++ b/packages/query-core/src/infiniteQueryBehavior.ts @@ -5,9 +5,8 @@ import { ensureQueryFn, } from './utils' import type { + FetchPageDirectionMode, InfiniteData, - InfiniteQueryPageParamsDeclarativeOptions, - InfiniteQueryPageParamsManualOptions, InfiniteQueryPageParamsOptions, OmitKeyof, QueryFunctionContext, @@ -15,14 +14,22 @@ import type { } from './types' import type { QueryBehavior } from './query' -export function infiniteQueryBehavior( +export function infiniteQueryBehavior< + TQueryFnData, + TError, + TData, + TPageParam, + TMode extends FetchPageDirectionMode = FetchPageDirectionMode, +>( pages?: number, ): QueryBehavior> { return { onFetch: (context, query) => { - const options = context.options as - | InfiniteQueryPageParamsManualOptions - | InfiniteQueryPageParamsDeclarativeOptions + const options = context.options as InfiniteQueryPageParamsOptions< + TQueryFnData, + TPageParam, + TMode + > const fetchMore = context.fetchOptions?.meta?.fetchMore const oldPages = context.state.data?.pages || [] const oldPageParams = context.state.data?.pageParams || [] diff --git a/packages/query-core/src/infiniteQueryObserver.ts b/packages/query-core/src/infiniteQueryObserver.ts index d14285758ef..2443f4af15d 100644 --- a/packages/query-core/src/infiniteQueryObserver.ts +++ b/packages/query-core/src/infiniteQueryObserver.ts @@ -13,8 +13,8 @@ import type { InfiniteQueryFetchNextPageArgs, InfiniteQueryFetchPreviousPageArgs, InfiniteQueryMode, - InfiniteQueryObserverOptionsBase, InfiniteQueryObserverOptions, + InfiniteQueryObserverOptionsBase, InfiniteQueryObserverResult, QueryKey, QueryObserverOptions, From 44b6f7873d3e20cc22f02427544d98d9e3085f62 Mon Sep 17 00:00:00 2001 From: TkDodo Date: Fri, 3 Apr 2026 22:45:44 +0200 Subject: [PATCH 25/25] do not change remaining pages --- packages/query-core/src/infiniteQueryBehavior.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/query-core/src/infiniteQueryBehavior.ts b/packages/query-core/src/infiniteQueryBehavior.ts index d512e11cc60..b6578a2ca9c 100644 --- a/packages/query-core/src/infiniteQueryBehavior.ts +++ b/packages/query-core/src/infiniteQueryBehavior.ts @@ -105,7 +105,7 @@ export function infiniteQueryBehavior< result = await fetchPage(oldData, param, previous) } else { - const remainingPages = pages ?? Math.max(oldPages.length, 1) + const remainingPages = pages ?? oldPages.length // Fetch all pages do {