@@ -116,45 +116,6 @@ export type QueryLifecycleQueryExtraOptions<
116116 ) : Promise < void > | void
117117}
118118
119- /**
120- * @since 2.4.0
121- * @public
122- */
123- export type TypedOnQueryStarted <
124- ResultType ,
125- QueryArg ,
126- BaseQuery extends BaseQueryFn ,
127- ReducerPath extends string = string ,
128- DefinitionType extends 'query' | 'mutation' = 'query' | 'mutation' ,
129- > = 'query' | 'mutation' extends DefinitionType
130- ? QueryLifecycleMutationExtraOptions <
131- ResultType ,
132- QueryArg ,
133- BaseQuery ,
134- ReducerPath
135- > [ 'onQueryStarted' ] &
136- QueryLifecycleQueryExtraOptions <
137- ResultType ,
138- QueryArg ,
139- BaseQuery ,
140- ReducerPath
141- > [ 'onQueryStarted' ]
142- : 'query' extends DefinitionType
143- ? QueryLifecycleQueryExtraOptions <
144- ResultType ,
145- QueryArg ,
146- BaseQuery ,
147- ReducerPath
148- > [ 'onQueryStarted' ]
149- : 'mutation' extends DefinitionType
150- ? QueryLifecycleMutationExtraOptions <
151- ResultType ,
152- QueryArg ,
153- BaseQuery ,
154- ReducerPath
155- > [ 'onQueryStarted' ]
156- : never
157-
158119export type QueryLifecycleMutationExtraOptions <
159120 ResultType ,
160121 QueryArg ,
@@ -231,6 +192,212 @@ export type MutationLifecycleApi<
231192> = MutationBaseLifecycleApi < QueryArg , BaseQuery , ResultType , ReducerPath > &
232193 QueryLifecyclePromises < ResultType , BaseQuery >
233194
195+ /**
196+ * Provides a way to define a strongly-typed version of
197+ * {@linkcode QueryLifecycleQueryExtraOptions.onQueryStarted | onQueryStarted}
198+ * for a specific query.
199+ *
200+ * @example
201+ * <caption>#### __Create and reuse a strongly-typed `onQueryStarted` function__</caption>
202+ *
203+ * ```ts
204+ * import type { TypedOnQueryStartedForQueryEndpoints } from '@reduxjs/toolkit/query'
205+ * import { createApi, fetchBaseQuery } from '@reduxjs/toolkit/query'
206+ *
207+ * type Post = {
208+ * id: number
209+ * title: string
210+ * userId: number
211+ * }
212+ *
213+ * type PostsApiResponse = {
214+ * posts: Post[]
215+ * total: number
216+ * skip: number
217+ * limit: number
218+ * }
219+ *
220+ * type QueryArgument = number | undefined
221+ *
222+ * type BaseQueryFunction = ReturnType<typeof fetchBaseQuery>
223+ *
224+ * const baseApiSlice = createApi({
225+ * baseQuery: fetchBaseQuery({ baseUrl: 'https://dummyjson.com' }),
226+ * reducerPath: 'postsApi',
227+ * tagTypes: ['Posts'],
228+ * endpoints: (builder) => ({
229+ * getPosts: builder.query<PostsApiResponse, void>({
230+ * query: () => `/posts`,
231+ * }),
232+ *
233+ * getPostById: builder.query<Post, QueryArgument>({
234+ * query: (postId) => `/posts/${postId}`,
235+ * }),
236+ * }),
237+ * })
238+ *
239+ * const updatePostOnFulfilled: TypedOnQueryStartedForQueryEndpoints<
240+ * PostsApiResponse,
241+ * QueryArgument,
242+ * BaseQueryFunction,
243+ * 'postsApi'
244+ * > = async (queryArgument, { dispatch, queryFulfilled }) => {
245+ * const result = await queryFulfilled
246+ *
247+ * const { posts } = result.data
248+ *
249+ * // Pre-fill the individual post entries with the results
250+ * // from the list endpoint query
251+ * dispatch(
252+ * baseApiSlice.util.upsertQueryEntries(
253+ * posts.map((post) => ({
254+ * endpointName: 'getPostById',
255+ * arg: post.id,
256+ * value: post,
257+ * })),
258+ * ),
259+ * )
260+ * }
261+ *
262+ * export const extendedApiSlice = baseApiSlice.injectEndpoints({
263+ * endpoints: (builder) => ({
264+ * getPostsByUserId: builder.query<PostsApiResponse, QueryArgument>({
265+ * query: (userId) => `/posts/user/${userId}`,
266+ *
267+ * onQueryStarted: updatePostOnFulfilled,
268+ * }),
269+ * }),
270+ * })
271+ * ```
272+ *
273+ * @template ResultType - The type of the result `data` returned by the query.
274+ * @template QueryArgumentType - The type of the argument passed into the query.
275+ * @template BaseQueryFunctionType - The type of the base query function being used.
276+ * @template ReducerPath - The type representing the `reducerPath` for the API slice.
277+ *
278+ * @since 2.4.0
279+ * @public
280+ */
281+ export type TypedOnQueryStartedForQueryEndpoints <
282+ ResultType ,
283+ QueryArgumentType ,
284+ BaseQueryFunctionType extends BaseQueryFn ,
285+ ReducerPath extends string = string ,
286+ > = QueryLifecycleQueryExtraOptions <
287+ ResultType ,
288+ QueryArgumentType ,
289+ BaseQueryFunctionType ,
290+ ReducerPath
291+ > [ 'onQueryStarted' ]
292+
293+ /**
294+ * Provides a way to define a strongly-typed version of
295+ * {@linkcode QueryLifecycleMutationExtraOptions.onQueryStarted | onQueryStarted}
296+ * for a specific mutation.
297+ *
298+ * @example
299+ * <caption>#### __Create and reuse a strongly-typed `onQueryStarted` function__</caption>
300+ *
301+ * ```ts
302+ * import type { TypedOnQueryStartedForMutationEndpoints } from '@reduxjs/toolkit/query'
303+ * import { createApi, fetchBaseQuery } from '@reduxjs/toolkit/query'
304+ *
305+ * type Post = {
306+ * id: number
307+ * title: string
308+ * userId: number
309+ * }
310+ *
311+ * type PostsApiResponse = {
312+ * posts: Post[]
313+ * total: number
314+ * skip: number
315+ * limit: number
316+ * }
317+ *
318+ * type QueryArgument = Pick<Post, 'id'> & Partial<Post>
319+ *
320+ * type BaseQueryFunction = ReturnType<typeof fetchBaseQuery>
321+ *
322+ * const baseApiSlice = createApi({
323+ * baseQuery: fetchBaseQuery({ baseUrl: 'https://dummyjson.com' }),
324+ * reducerPath: 'postsApi',
325+ * tagTypes: ['Posts'],
326+ * endpoints: (builder) => ({
327+ * getPosts: builder.query<PostsApiResponse, void>({
328+ * query: () => `/posts`,
329+ * }),
330+ *
331+ * getPostById: builder.query<Post, number>({
332+ * query: (postId) => `/posts/${postId}`,
333+ * }),
334+ * }),
335+ * })
336+ *
337+ * const updatePostOnFulfilled: TypedOnQueryStartedForMutationEndpoints<
338+ * Post,
339+ * QueryArgument,
340+ * BaseQueryFunction,
341+ * 'postsApi'
342+ * > = async ({ id, ...patch }, { dispatch, queryFulfilled }) => {
343+ * const patchCollection = dispatch(
344+ * baseApiSlice.util.updateQueryData('getPostById', id, (draftPost) => {
345+ * Object.assign(draftPost, patch)
346+ * }),
347+ * )
348+ *
349+ * try {
350+ * await queryFulfilled
351+ * } catch {
352+ * patchCollection.undo()
353+ * }
354+ * }
355+ *
356+ * export const extendedApiSlice = baseApiSlice.injectEndpoints({
357+ * endpoints: (builder) => ({
358+ * addPost: builder.mutation<Post, Omit<QueryArgument, 'id'>>({
359+ * query: (body) => ({
360+ * url: `posts/add`,
361+ * method: 'POST',
362+ * body,
363+ * }),
364+ *
365+ * onQueryStarted: updatePostOnFulfilled,
366+ * }),
367+ *
368+ * updatePost: builder.mutation<Post, QueryArgument>({
369+ * query: ({ id, ...patch }) => ({
370+ * url: `post/${id}`,
371+ * method: 'PATCH',
372+ * body: patch,
373+ * }),
374+ *
375+ * onQueryStarted: updatePostOnFulfilled,
376+ * }),
377+ * }),
378+ * })
379+ * ```
380+ *
381+ * @template ResultType - The type of the result `data` returned by the query.
382+ * @template QueryArgumentType - The type of the argument passed into the query.
383+ * @template BaseQueryFunctionType - The type of the base query function being used.
384+ * @template ReducerPath - The type representing the `reducerPath` for the API slice.
385+ *
386+ * @since 2.4.0
387+ * @public
388+ */
389+ export type TypedOnQueryStartedForMutationEndpoints <
390+ ResultType ,
391+ QueryArgumentType ,
392+ BaseQueryFunctionType extends BaseQueryFn ,
393+ ReducerPath extends string = string ,
394+ > = QueryLifecycleMutationExtraOptions <
395+ ResultType ,
396+ QueryArgumentType ,
397+ BaseQueryFunctionType ,
398+ ReducerPath
399+ > [ 'onQueryStarted' ]
400+
234401export const buildQueryLifecycleHandler : InternalHandlerBuilder = ( {
235402 api,
236403 context,
0 commit comments