From 56e2af89aef745f71af6fdc2d4804d44b4832f24 Mon Sep 17 00:00:00 2001 From: Mark Erikson Date: Sun, 2 Mar 2025 22:42:23 -0500 Subject: [PATCH 1/4] Fix infinite query matcher types --- docs/rtk-query/usage/infinite-queries.mdx | 2 +- .../toolkit/src/query/core/buildThunks.ts | 29 ++++++++++++++----- .../src/query/tests/infiniteQueries.test-d.ts | 17 +++++++++++ 3 files changed, 39 insertions(+), 9 deletions(-) diff --git a/docs/rtk-query/usage/infinite-queries.mdx b/docs/rtk-query/usage/infinite-queries.mdx index 0d0ec7e9c5..c43dd02572 100644 --- a/docs/rtk-query/usage/infinite-queries.mdx +++ b/docs/rtk-query/usage/infinite-queries.mdx @@ -63,7 +63,7 @@ This structure allows flexibility in how your UI chooses to render the data (sho Infinite query endpoints are defined by returning an object inside the `endpoints` section of `createApi`, and defining the fields using the `build.infiniteQuery()` method. They are an extension of standard query endpoints - you can specify [the same options as standard queries](./queries.mdx#defining-query-endpoints) (providing either `query` or `queryFn`, customizing with `transformResponse`, lifecycles with `onCacheEntryAdded` and `onQueryStarted`, defining tags, etc). However, they also require an additional `infiniteQueryOptions` field to specify the infinite query behavior. -With TypeScript, you must supply 3 generic arguments: `build.infiniteQuery`, where `ResultType` is the contents of a single page, `QueryArg` is the type passed in as the cache key, and `PageParam` is the value that will be passed to `query/queryFn` to make the rest. If there is no argument, use `void` for the arg type instead. +With TypeScript, you must supply 3 generic arguments: `build.infiniteQuery`, where `ResultType` is the contents of a single page, `QueryArg` is the type passed in as the cache key, and `PageParam` is the value used to request a specific page. If there is no argument, use `void` for the arg type instead. ### `infiniteQueryOptions` diff --git a/packages/toolkit/src/query/core/buildThunks.ts b/packages/toolkit/src/query/core/buildThunks.ts index 37f6606c7c..1d753bd5d0 100644 --- a/packages/toolkit/src/query/core/buildThunks.ts +++ b/packages/toolkit/src/query/core/buildThunks.ts @@ -71,14 +71,14 @@ export type BuildThunksApiEndpointQuery< export type BuildThunksApiEndpointInfiniteQuery< Definition extends InfiniteQueryDefinition, -> = Matchers +> = Matchers, Definition> export type BuildThunksApiEndpointMutation< Definition extends MutationDefinition, > = Matchers type EndpointThunk< - Thunk extends QueryThunk | MutationThunk, + Thunk extends QueryThunk | MutationThunk | InfiniteQueryThunk, Definition extends EndpointDefinition, > = Definition extends EndpointDefinition< @@ -94,27 +94,41 @@ type EndpointThunk< ATConfig & { rejectValue: BaseQueryError } > : never - : never + : Definition extends InfiniteQueryDefinition< + infer QueryArg, + infer PageParam, + infer BaseQueryFn, + any, + infer ResultType + > + ? Thunk extends AsyncThunk + ? AsyncThunk< + InfiniteData, + ATArg & { originalArgs: QueryArg }, + ATConfig & { rejectValue: BaseQueryError } + > + : never + : never export type PendingAction< - Thunk extends QueryThunk | MutationThunk, + Thunk extends QueryThunk | MutationThunk | InfiniteQueryThunk, Definition extends EndpointDefinition, > = ReturnType['pending']> export type FulfilledAction< - Thunk extends QueryThunk | MutationThunk, + Thunk extends QueryThunk | MutationThunk | InfiniteQueryThunk, Definition extends EndpointDefinition, > = ReturnType['fulfilled']> export type RejectedAction< - Thunk extends QueryThunk | MutationThunk, + Thunk extends QueryThunk | MutationThunk | InfiniteQueryThunk, Definition extends EndpointDefinition, > = ReturnType['rejected']> export type Matcher = (value: any) => value is M export interface Matchers< - Thunk extends QueryThunk | MutationThunk, + Thunk extends QueryThunk | MutationThunk | InfiniteQueryThunk, Definition extends EndpointDefinition, > { matchPending: Matcher> @@ -645,7 +659,6 @@ export function buildThunks< isForcedQueryNeedingRefetch || !cachedData ? blankData : cachedData ) as InfiniteData - // If the thunk specified a direction and we do have at least one page, // fetch the next or previous page if ('direction' in arg && arg.direction && existingData.pages.length) { diff --git a/packages/toolkit/src/query/tests/infiniteQueries.test-d.ts b/packages/toolkit/src/query/tests/infiniteQueries.test-d.ts index e305b5e788..8bd332ad0c 100644 --- a/packages/toolkit/src/query/tests/infiniteQueries.test-d.ts +++ b/packages/toolkit/src/query/tests/infiniteQueries.test-d.ts @@ -5,6 +5,7 @@ import { QueryStatus, } from '@reduxjs/toolkit/query/react' import { setupApiStore } from '../../tests/utils/helpers' +import { createSlice } from '@internal/createSlice' describe('Infinite queries', () => { test('Basic infinite query behavior', async () => { @@ -68,6 +69,22 @@ describe('Infinite queries', () => { expectTypeOf(pokemonApi.useGetInfinitePokemonInfiniteQuery).toBeFunction() + const slice = createSlice({ + name: 'pokemon', + initialState: {} as { data: Pokemon[] }, + reducers: {}, + extraReducers: (builder) => { + builder.addMatcher( + pokemonApi.endpoints.getInfinitePokemon.matchFulfilled, + (state, action) => { + expectTypeOf(action.payload).toEqualTypeOf< + InfiniteData + >() + }, + ) + }, + }) + const res = storeRef.store.dispatch( pokemonApi.endpoints.getInfinitePokemon.initiate('fire', {}), ) From a05ebe43f1218c48f12c5b22484cde5a658531a6 Mon Sep 17 00:00:00 2001 From: Mark Erikson Date: Sun, 2 Mar 2025 22:45:31 -0500 Subject: [PATCH 2/4] Fix infinite query providesTags types --- packages/toolkit/src/query/endpointDefinitions.ts | 2 +- packages/toolkit/src/query/tests/infiniteQueries.test-d.ts | 6 ++++++ 2 files changed, 7 insertions(+), 1 deletion(-) diff --git a/packages/toolkit/src/query/endpointDefinitions.ts b/packages/toolkit/src/query/endpointDefinitions.ts index 44e46e3519..d69b976d21 100644 --- a/packages/toolkit/src/query/endpointDefinitions.ts +++ b/packages/toolkit/src/query/endpointDefinitions.ts @@ -595,7 +595,7 @@ export interface InfiniteQueryExtraOptions< providesTags?: ResultDescription< TagTypes, - ResultType, + InfiniteData, QueryArg, BaseQueryError, BaseQueryMeta diff --git a/packages/toolkit/src/query/tests/infiniteQueries.test-d.ts b/packages/toolkit/src/query/tests/infiniteQueries.test-d.ts index 8bd332ad0c..132ebd12c8 100644 --- a/packages/toolkit/src/query/tests/infiniteQueries.test-d.ts +++ b/packages/toolkit/src/query/tests/infiniteQueries.test-d.ts @@ -55,6 +55,12 @@ describe('Infinite queries', () => { InfiniteData >() }, + providesTags: (result) => { + expectTypeOf(result).toEqualTypeOf< + InfiniteData | undefined + >() + return [] + }, }), }), }) From a2ac21df2849dbd34e76ae7cd9c99ab871fc6b2b Mon Sep 17 00:00:00 2001 From: Mark Erikson Date: Sun, 2 Mar 2025 23:06:59 -0500 Subject: [PATCH 3/4] Fix infinite query subscription/state hooks --- packages/toolkit/src/query/react/buildHooks.ts | 4 ++-- .../src/query/tests/infiniteQueries.test-d.ts | 14 ++++++++++++++ 2 files changed, 16 insertions(+), 2 deletions(-) diff --git a/packages/toolkit/src/query/react/buildHooks.ts b/packages/toolkit/src/query/react/buildHooks.ts index 1f08eab47b..4c8ca51bef 100644 --- a/packages/toolkit/src/query/react/buildHooks.ts +++ b/packages/toolkit/src/query/react/buildHooks.ts @@ -940,7 +940,7 @@ export type UseInfiniteQuery< export type UseInfiniteQueryState< D extends InfiniteQueryDefinition, > = = UseInfiniteQueryStateDefaultResult>( - arg: QueryArgFrom | SkipToken, + arg: InfiniteQueryArgFrom | SkipToken, options?: UseInfiniteQueryStateOptions, ) => UseInfiniteQueryStateResult @@ -977,7 +977,7 @@ export type TypedUseInfiniteQueryState< export type UseInfiniteQuerySubscription< D extends InfiniteQueryDefinition, > = ( - arg: QueryArgFrom | SkipToken, + arg: InfiniteQueryArgFrom | SkipToken, options?: UseInfiniteQuerySubscriptionOptions, ) => UseInfiniteQuerySubscriptionResult diff --git a/packages/toolkit/src/query/tests/infiniteQueries.test-d.ts b/packages/toolkit/src/query/tests/infiniteQueries.test-d.ts index 132ebd12c8..2ada1aef09 100644 --- a/packages/toolkit/src/query/tests/infiniteQueries.test-d.ts +++ b/packages/toolkit/src/query/tests/infiniteQueries.test-d.ts @@ -75,6 +75,20 @@ describe('Infinite queries', () => { expectTypeOf(pokemonApi.useGetInfinitePokemonInfiniteQuery).toBeFunction() + expectTypeOf(pokemonApi.endpoints.getInfinitePokemon.useInfiniteQuery) + .parameter(0) + .toEqualTypeOf() + + expectTypeOf(pokemonApi.endpoints.getInfinitePokemon.useInfiniteQueryState) + .parameter(0) + .toEqualTypeOf() + + expectTypeOf( + pokemonApi.endpoints.getInfinitePokemon.useInfiniteQuerySubscription, + ) + .parameter(0) + .toEqualTypeOf() + const slice = createSlice({ name: 'pokemon', initialState: {} as { data: Pokemon[] }, From 5f2178e19af15d033e733967b29de9c4a041558c Mon Sep 17 00:00:00 2001 From: Mark Erikson Date: Sun, 2 Mar 2025 23:13:06 -0500 Subject: [PATCH 4/4] Fix internal providedBy type issue --- packages/toolkit/src/query/core/buildThunks.ts | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/packages/toolkit/src/query/core/buildThunks.ts b/packages/toolkit/src/query/core/buildThunks.ts index 1d753bd5d0..81ca5d38a7 100644 --- a/packages/toolkit/src/query/core/buildThunks.ts +++ b/packages/toolkit/src/query/core/buildThunks.ts @@ -26,6 +26,7 @@ import type { PageParamFrom, QueryArgFrom, QueryDefinition, + ResultDescription, ResultTypeFrom, } from '../endpointDefinitions' import { @@ -973,14 +974,18 @@ export function getPreviousPageParam( export function calculateProvidedByThunk( action: UnwrapPromise< - ReturnType> | ReturnType> + | ReturnType> + | ReturnType> + | ReturnType>> >, type: 'providesTags' | 'invalidatesTags', endpointDefinitions: EndpointDefinitions, assertTagType: AssertTagTypes, ) { return calculateProvidedBy( - endpointDefinitions[action.meta.arg.endpointName][type], + endpointDefinitions[action.meta.arg.endpointName][ + type + ] as ResultDescription, isFulfilled(action) ? action.payload : undefined, isRejectedWithValue(action) ? action.payload : undefined, action.meta.arg.originalArgs,