Skip to content

Commit 356627a

Browse files
fix(types): adds queryKey generic to queryClient functions (#2100)
* fix(types): adds queryKey generic to queryClient functions * test(clientQuery): adds tests for type-errors when using strict keys
1 parent e81cb47 commit 356627a

File tree

3 files changed

+156
-58
lines changed

3 files changed

+156
-58
lines changed

src/core/queryClient.ts

Lines changed: 75 additions & 53 deletions
Original file line numberDiff line numberDiff line change
@@ -252,24 +252,24 @@ export class QueryClient {
252252
return promise
253253
}
254254

255-
fetchQuery<TQueryFnData = unknown, TError = unknown, TData = TQueryFnData>(
256-
options: FetchQueryOptions<TQueryFnData, TError, TData>
255+
fetchQuery<TQueryFnData = unknown, TError = unknown, TData = TQueryFnData, TQueryKey extends QueryKey = QueryKey>(
256+
options: FetchQueryOptions<TQueryFnData, TError, TData, TQueryKey>
257257
): Promise<TData>
258-
fetchQuery<TQueryFnData = unknown, TError = unknown, TData = TQueryFnData>(
259-
queryKey: QueryKey,
260-
options?: FetchQueryOptions<TQueryFnData, TError, TData>
258+
fetchQuery<TQueryFnData = unknown, TError = unknown, TData = TQueryFnData, TQueryKey extends QueryKey = QueryKey>(
259+
queryKey: TQueryKey,
260+
options?: FetchQueryOptions<TQueryFnData, TError, TData, TQueryKey>
261261
): Promise<TData>
262-
fetchQuery<TQueryFnData = unknown, TError = unknown, TData = TQueryFnData>(
263-
queryKey: QueryKey,
264-
queryFn: QueryFunction<TQueryFnData>,
265-
options?: FetchQueryOptions<TQueryFnData, TError, TData>
262+
fetchQuery<TQueryFnData = unknown, TError = unknown, TData = TQueryFnData, TQueryKey extends QueryKey = QueryKey>(
263+
queryKey: TQueryKey,
264+
queryFn: QueryFunction<TQueryFnData, TQueryKey>,
265+
options?: FetchQueryOptions<TQueryFnData, TError, TData, TQueryKey>
266266
): Promise<TData>
267-
fetchQuery<TQueryFnData, TError, TData = TQueryFnData>(
268-
arg1: QueryKey | FetchQueryOptions<TQueryFnData, TError, TData>,
267+
fetchQuery<TQueryFnData, TError, TData = TQueryFnData, TQueryKey extends QueryKey = QueryKey>(
268+
arg1: TQueryKey | FetchQueryOptions<TQueryFnData, TError, TData, TQueryKey>,
269269
arg2?:
270-
| QueryFunction<TQueryFnData>
271-
| FetchQueryOptions<TQueryFnData, TError, TData>,
272-
arg3?: FetchQueryOptions<TQueryFnData, TError, TData>
270+
| QueryFunction<TQueryFnData, TQueryKey>
271+
| FetchQueryOptions<TQueryFnData, TError, TData, TQueryKey>,
272+
arg3?: FetchQueryOptions<TQueryFnData, TError, TData, TQueryKey>
273273
): Promise<TData> {
274274
const parsedOptions = parseQueryArgs(arg1, arg2, arg3)
275275
const defaultedOptions = this.defaultQueryOptions(parsedOptions)
@@ -286,22 +286,22 @@ export class QueryClient {
286286
: Promise.resolve(query.state.data as TData)
287287
}
288288

289-
prefetchQuery<TQueryFnData = unknown, TError = unknown, TData = TQueryFnData>(
290-
options: FetchQueryOptions<TQueryFnData, TError, TData>
289+
prefetchQuery<TQueryFnData = unknown, TError = unknown, TData = TQueryFnData, TQueryKey extends QueryKey = QueryKey>(
290+
options: FetchQueryOptions<TQueryFnData, TError, TData, TQueryKey>
291291
): Promise<void>
292-
prefetchQuery<TQueryFnData = unknown, TError = unknown, TData = TQueryFnData>(
293-
queryKey: QueryKey,
294-
options?: FetchQueryOptions<TQueryFnData, TError, TData>
292+
prefetchQuery<TQueryFnData = unknown, TError = unknown, TData = TQueryFnData, TQueryKey extends QueryKey = QueryKey>(
293+
queryKey: TQueryKey,
294+
options?: FetchQueryOptions<TQueryFnData, TError, TData, TQueryKey>
295295
): Promise<void>
296-
prefetchQuery<TQueryFnData = unknown, TError = unknown, TData = TQueryFnData>(
297-
queryKey: QueryKey,
298-
queryFn: QueryFunction,
299-
options?: FetchQueryOptions<TQueryFnData, TError, TData>
296+
prefetchQuery<TQueryFnData = unknown, TError = unknown, TData = TQueryFnData, TQueryKey extends QueryKey = QueryKey>(
297+
queryKey: TQueryKey,
298+
queryFn: QueryFunction<TQueryFnData, TQueryKey>,
299+
options?: FetchQueryOptions<TQueryFnData, TError, TData, TQueryKey>
300300
): Promise<void>
301-
prefetchQuery<TQueryFnData = unknown, TError = unknown, TData = TQueryFnData>(
302-
arg1: QueryKey | FetchQueryOptions<TQueryFnData, TError, TData>,
303-
arg2?: QueryFunction | FetchQueryOptions<TQueryFnData, TError, TData>,
304-
arg3?: FetchQueryOptions<TQueryFnData, TError, TData>
301+
prefetchQuery<TQueryFnData = unknown, TError = unknown, TData = TQueryFnData, TQueryKey extends QueryKey = QueryKey>(
302+
arg1: TQueryKey | FetchQueryOptions<TQueryFnData, TError, TData, TQueryKey>,
303+
arg2?: QueryFunction<TQueryFnData, TQueryKey> | FetchQueryOptions<TQueryFnData, TError, TData, TQueryKey>,
304+
arg3?: FetchQueryOptions<TQueryFnData, TError, TData, TQueryKey>
305305
): Promise<void> {
306306
return this.fetchQuery(arg1 as any, arg2 as any, arg3)
307307
.then(noop)
@@ -311,33 +311,36 @@ export class QueryClient {
311311
fetchInfiniteQuery<
312312
TQueryFnData = unknown,
313313
TError = unknown,
314-
TData = TQueryFnData
314+
TData = TQueryFnData,
315+
TQueryKey extends QueryKey = QueryKey
315316
>(
316-
options: FetchInfiniteQueryOptions<TQueryFnData, TError, TData>
317+
options: FetchInfiniteQueryOptions<TQueryFnData, TError, TData, TQueryKey>
317318
): Promise<InfiniteData<TData>>
318319
fetchInfiniteQuery<
319320
TQueryFnData = unknown,
320321
TError = unknown,
321-
TData = TQueryFnData
322+
TData = TQueryFnData,
323+
TQueryKey extends QueryKey = QueryKey
322324
>(
323-
queryKey: QueryKey,
324-
options?: FetchInfiniteQueryOptions<TQueryFnData, TError, TData>
325+
queryKey: TQueryKey,
326+
options?: FetchInfiniteQueryOptions<TQueryFnData, TError, TData, TQueryKey>
325327
): Promise<InfiniteData<TData>>
326328
fetchInfiniteQuery<
327329
TQueryFnData = unknown,
328330
TError = unknown,
329-
TData = TQueryFnData
331+
TData = TQueryFnData,
332+
TQueryKey extends QueryKey = QueryKey
330333
>(
331-
queryKey: QueryKey,
332-
queryFn: QueryFunction<TQueryFnData>,
333-
options?: FetchInfiniteQueryOptions<TQueryFnData, TError, TData>
334+
queryKey: TQueryKey,
335+
queryFn: QueryFunction<TQueryFnData, TQueryKey>,
336+
options?: FetchInfiniteQueryOptions<TQueryFnData, TError, TData, TQueryKey>
334337
): Promise<InfiniteData<TData>>
335-
fetchInfiniteQuery<TQueryFnData, TError, TData = TQueryFnData>(
336-
arg1: QueryKey | FetchInfiniteQueryOptions<TQueryFnData, TError, TData>,
338+
fetchInfiniteQuery<TQueryFnData, TError, TData = TQueryFnData, TQueryKey extends QueryKey = QueryKey>(
339+
arg1: TQueryKey | FetchInfiniteQueryOptions<TQueryFnData, TError, TData, TQueryKey>,
337340
arg2?:
338-
| QueryFunction<TQueryFnData>
339-
| FetchInfiniteQueryOptions<TQueryFnData, TError, TData>,
340-
arg3?: FetchInfiniteQueryOptions<TQueryFnData, TError, TData>
341+
| QueryFunction<TQueryFnData, TQueryKey>
342+
| FetchInfiniteQueryOptions<TQueryFnData, TError, TData, TQueryKey>,
343+
arg3?: FetchInfiniteQueryOptions<TQueryFnData, TError, TData, TQueryKey>
341344
): Promise<InfiniteData<TData>> {
342345
const parsedOptions = parseQueryArgs(arg1, arg2, arg3)
343346
parsedOptions.behavior = infiniteQueryBehavior<
@@ -348,20 +351,39 @@ export class QueryClient {
348351
return this.fetchQuery(parsedOptions)
349352
}
350353

351-
prefetchInfiniteQuery(options: FetchInfiniteQueryOptions): Promise<void>
352-
prefetchInfiniteQuery(
353-
queryKey: QueryKey,
354-
options?: FetchInfiniteQueryOptions
354+
prefetchInfiniteQuery<
355+
TQueryFnData = unknown,
356+
TError = unknown,
357+
TData = TQueryFnData,
358+
TQueryKey extends QueryKey = QueryKey
359+
>(
360+
options: FetchInfiniteQueryOptions<TQueryFnData, TError, TData, TQueryKey>
355361
): Promise<void>
356-
prefetchInfiniteQuery(
357-
queryKey: QueryKey,
358-
queryFn: QueryFunction,
359-
options?: FetchInfiniteQueryOptions
362+
prefetchInfiniteQuery<
363+
TQueryFnData = unknown,
364+
TError = unknown,
365+
TData = TQueryFnData,
366+
TQueryKey extends QueryKey = QueryKey
367+
>(
368+
queryKey: TQueryKey,
369+
options?: FetchInfiniteQueryOptions<TQueryFnData, TError, TData, TQueryKey>
360370
): Promise<void>
361-
prefetchInfiniteQuery(
362-
arg1: QueryKey | FetchInfiniteQueryOptions,
363-
arg2?: QueryFunction | FetchInfiniteQueryOptions,
364-
arg3?: FetchInfiniteQueryOptions
371+
prefetchInfiniteQuery<
372+
TQueryFnData = unknown,
373+
TError = unknown,
374+
TData = TQueryFnData,
375+
TQueryKey extends QueryKey = QueryKey
376+
>(
377+
queryKey: TQueryKey,
378+
queryFn: QueryFunction<TQueryFnData, TQueryKey>,
379+
options?: FetchInfiniteQueryOptions<TQueryFnData, TError, TData, TQueryKey>
380+
): Promise<void>
381+
prefetchInfiniteQuery<TQueryFnData, TError, TData = TQueryFnData, TQueryKey extends QueryKey = QueryKey>(
382+
arg1: TQueryKey | FetchInfiniteQueryOptions<TQueryFnData, TError, TData, TQueryKey>,
383+
arg2?:
384+
| QueryFunction<TQueryFnData, TQueryKey>
385+
| FetchInfiniteQueryOptions<TQueryFnData, TError, TData, TQueryKey>,
386+
arg3?: FetchInfiniteQueryOptions<TQueryFnData, TError, TData, TQueryKey>
365387
): Promise<void> {
366388
return this.fetchInfiniteQuery(arg1 as any, arg2 as any, arg3)
367389
.then(noop)

src/core/tests/queryClient.test.tsx

Lines changed: 75 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
import { sleep, queryKey, mockConsoleError } from '../../react/tests/utils'
2-
import { QueryCache, QueryClient, QueryObserver } from '../..'
2+
import { QueryCache, QueryClient, QueryFunction, QueryObserver } from '../..'
33

44
describe('queryClient', () => {
55
let queryClient: QueryClient
@@ -186,6 +186,23 @@ describe('queryClient', () => {
186186
})
187187

188188
describe('fetchQuery', () => {
189+
test('should not type-error with strict query key', async () => {
190+
type StrictData = 'data'
191+
type StrictQueryKey = ['strict', string]
192+
const key: StrictQueryKey = ['strict', queryKey()]
193+
194+
const fetchFn: QueryFunction<StrictData, StrictQueryKey> = () => (
195+
Promise.resolve('data')
196+
)
197+
198+
await expect(
199+
queryClient.fetchQuery<StrictData, any, StrictData, StrictQueryKey>(
200+
key,
201+
fetchFn,
202+
)
203+
).resolves.toEqual('data')
204+
})
205+
189206
// https://github.com/tannerlinsley/react-query/issues/652
190207
test('should not retry by default', async () => {
191208
const consoleMock = mockConsoleError()
@@ -282,6 +299,28 @@ describe('queryClient', () => {
282299
})
283300

284301
describe('fetchInfiniteQuery', () => {
302+
test('should not type-error with strict query key', async () => {
303+
type StrictData = string
304+
type StrictQueryKey = ['strict', string]
305+
const key: StrictQueryKey = ['strict', queryKey()]
306+
307+
const data = {
308+
pages: ['data'],
309+
pageParams: [undefined],
310+
}
311+
312+
const fetchFn: QueryFunction<StrictData, StrictQueryKey> = () => (
313+
Promise.resolve(data.pages[0])
314+
)
315+
316+
await expect(
317+
queryClient.fetchInfiniteQuery<StrictData, any, StrictData, StrictQueryKey>(
318+
key,
319+
fetchFn,
320+
)
321+
).resolves.toEqual(data)
322+
})
323+
285324
test('should return infinite query data', async () => {
286325
const key = queryKey()
287326
const result = await queryClient.fetchInfiniteQuery(
@@ -301,6 +340,25 @@ describe('queryClient', () => {
301340
})
302341

303342
describe('prefetchInfiniteQuery', () => {
343+
test('should not type-error with strict query key', async () => {
344+
type StrictData = 'data'
345+
type StrictQueryKey = ['strict', string]
346+
const key: StrictQueryKey = ['strict', queryKey()]
347+
348+
const fetchFn: QueryFunction<StrictData, StrictQueryKey> = () => (
349+
Promise.resolve('data')
350+
)
351+
352+
await queryClient.prefetchInfiniteQuery<StrictData, any, StrictData, StrictQueryKey>(key, fetchFn)
353+
354+
const result = queryClient.getQueryData(key)
355+
356+
expect(result).toEqual({
357+
pages: ['data'],
358+
pageParams: [undefined],
359+
})
360+
})
361+
304362
test('should return infinite query data', async () => {
305363
const key = queryKey()
306364

@@ -318,6 +376,22 @@ describe('queryClient', () => {
318376
})
319377

320378
describe('prefetchQuery', () => {
379+
test('should not type-error with strict query key', async () => {
380+
type StrictData = 'data'
381+
type StrictQueryKey = ['strict', string]
382+
const key: StrictQueryKey = ['strict', queryKey()]
383+
384+
const fetchFn: QueryFunction<StrictData, StrictQueryKey> = () => (
385+
Promise.resolve('data')
386+
)
387+
388+
await queryClient.prefetchQuery<StrictData, any, StrictData, StrictQueryKey>(key, fetchFn);
389+
390+
const result = queryClient.getQueryData(key);
391+
392+
expect(result).toEqual('data')
393+
})
394+
321395
test('should return undefined when an error is thrown', async () => {
322396
const consoleMock = mockConsoleError()
323397

src/core/types.ts

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -210,8 +210,9 @@ export interface InfiniteQueryObserverOptions<
210210
export interface FetchQueryOptions<
211211
TQueryFnData = unknown,
212212
TError = unknown,
213-
TData = TQueryFnData
214-
> extends QueryOptions<TQueryFnData, TError, TData> {
213+
TData = TQueryFnData,
214+
TQueryKey extends QueryKey = QueryKey
215+
> extends QueryOptions<TQueryFnData, TError, TData, TQueryKey> {
215216
/**
216217
* The time in milliseconds after data is considered stale.
217218
* If the data is fresh it will be returned from the cache.
@@ -222,8 +223,9 @@ export interface FetchQueryOptions<
222223
export interface FetchInfiniteQueryOptions<
223224
TQueryFnData = unknown,
224225
TError = unknown,
225-
TData = TQueryFnData
226-
> extends FetchQueryOptions<TQueryFnData, TError, InfiniteData<TData>> {}
226+
TData = TQueryFnData,
227+
TQueryKey extends QueryKey = QueryKey
228+
> extends FetchQueryOptions<TQueryFnData, TError, InfiniteData<TData>, TQueryKey> {}
227229

228230
export interface ResultOptions {
229231
throwOnError?: boolean

0 commit comments

Comments
 (0)