Skip to content

Commit a991d92

Browse files
gwansikkautofix-ci[bot]TkDodo
authoredOct 11, 2024··
refactor(types): throw type error when skipToken is present in suspense query (#8082)
* refactor: handle type error when skipToken is present in suspense query * test(react-query): add skipToken test for query hooks * fix: merge form main * ci: apply automated fixes * feat(react-query): display “skipToken is not allowed” message in suspense hooks * feat(react-query): display “skipToken is not allowed” message in suspense hooks * feat(react-query): display “skipToken is not allowed” message in suspense hooks * fix(react-query): update error message in useSuspenseInfiniteQuery * test(react-query): add test case --------- Co-authored-by: autofix-ci[bot] <114827586+autofix-ci[bot]@users.noreply.github.com> Co-authored-by: Dominik Dorfmeister <office@dorfmeister.cc>
1 parent c635194 commit a991d92

11 files changed

+210
-39
lines changed
 

‎packages/react-query/src/__tests__/infiniteQueryOptions.test-d.tsx

+13-1
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
import { describe, expectTypeOf, it, test } from 'vitest'
2-
import { QueryClient, dataTagSymbol } from '@tanstack/query-core'
2+
import { QueryClient, dataTagSymbol, skipToken } from '@tanstack/query-core'
33
import { infiniteQueryOptions } from '../infiniteQueryOptions'
44
import { useInfiniteQuery } from '../useInfiniteQuery'
55
import { useSuspenseInfiniteQuery } from '../useSuspenseInfiniteQuery'
@@ -134,6 +134,18 @@ describe('infiniteQueryOptions', () => {
134134
InfiniteData<string, unknown> | undefined
135135
>()
136136
})
137+
it('should throw a type error when using queryFn with skipToken in a suspense query', () => {
138+
const options = infiniteQueryOptions({
139+
queryKey: ['key'],
140+
queryFn:
141+
Math.random() > 0.5 ? skipToken : () => Promise.resolve('string'),
142+
getNextPageParam: () => 1,
143+
initialPageParam: 1,
144+
})
145+
// @ts-expect-error TS2345
146+
const { data } = useSuspenseInfiniteQuery(options)
147+
expectTypeOf(data).toEqualTypeOf<InfiniteData<string, unknown>>()
148+
})
137149

138150
test('should not be allowed to be passed to non-infinite query functions', () => {
139151
const queryClient = new QueryClient()

‎packages/react-query/src/__tests__/prefetch.test.tsx

+7-3
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,11 @@ import {
1212
} from '..'
1313
import { createQueryClient, queryKey, renderWithClient, sleep } from './utils'
1414

15-
import type { InfiniteData, UseInfiniteQueryOptions, UseQueryOptions } from '..'
15+
import type {
16+
InfiniteData,
17+
UseSuspenseInfiniteQueryOptions,
18+
UseSuspenseQueryOptions,
19+
} from '..'
1620
import type { Mock } from 'vitest'
1721

1822
const generateQueryFn = (data: string) =>
@@ -56,7 +60,7 @@ describe('usePrefetchQuery', () => {
5660
const queryClient = createQueryClient({ queryCache })
5761

5862
function Suspended<TData = unknown>(props: {
59-
queryOpts: UseQueryOptions<TData, Error, TData, Array<string>>
63+
queryOpts: UseSuspenseQueryOptions<TData, Error, TData, Array<string>>
6064
children?: React.ReactNode
6165
}) {
6266
const state = useSuspenseQuery(props.queryOpts)
@@ -303,7 +307,7 @@ describe('usePrefetchInfiniteQuery', () => {
303307
const Fallback = vi.fn().mockImplementation(() => <div>Loading...</div>)
304308

305309
function Suspended<T = unknown>(props: {
306-
queryOpts: UseInfiniteQueryOptions<
310+
queryOpts: UseSuspenseInfiniteQueryOptions<
307311
T,
308312
Error,
309313
InfiniteData<T>,

‎packages/react-query/src/__tests__/queryOptions.test-d.tsx

+10
Original file line numberDiff line numberDiff line change
@@ -177,6 +177,16 @@ describe('queryOptions', () => {
177177
expectTypeOf(data).toEqualTypeOf<unknown>()
178178
})
179179

180+
it('should throw a type error when using queryFn with skipToken in a suspense query', () => {
181+
const options = queryOptions({
182+
queryKey: ['key'],
183+
queryFn: Math.random() > 0.5 ? skipToken : () => Promise.resolve(5),
184+
})
185+
// @ts-expect-error TS2345
186+
const { data } = useSuspenseQuery(options)
187+
expectTypeOf(data).toEqualTypeOf<number>()
188+
})
189+
180190
it('should return the proper type when passed to QueriesObserver', () => {
181191
const options = queryOptions({
182192
queryKey: ['key'],

‎packages/react-query/src/__tests__/suspense.test-d.tsx

+29-11
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
import { describe, expectTypeOf, it } from 'vitest'
2+
import { skipToken } from '@tanstack/query-core'
23
import { useSuspenseQuery } from '../useSuspenseQuery'
34
import { useSuspenseInfiniteQuery } from '../useSuspenseInfiniteQuery'
4-
import type { UseSuspenseQueryOptions } from '..'
55
import type { InfiniteData } from '@tanstack/query-core'
66

77
describe('useSuspenseQuery', () => {
@@ -23,6 +23,20 @@ describe('useSuspenseQuery', () => {
2323
expectTypeOf(status).toEqualTypeOf<'error' | 'success'>()
2424
})
2525

26+
it('should not allow skipToken in queryFn', () => {
27+
useSuspenseQuery({
28+
queryKey: ['key'],
29+
// @ts-expect-error
30+
queryFn: skipToken,
31+
})
32+
33+
useSuspenseQuery({
34+
queryKey: ['key'],
35+
// @ts-expect-error
36+
queryFn: Math.random() > 0.5 ? skipToken : () => Promise.resolve(5),
37+
})
38+
})
39+
2640
it('should not allow placeholderData, enabled or throwOnError props', () => {
2741
useSuspenseQuery({
2842
queryKey: ['key'],
@@ -70,6 +84,20 @@ describe('useSuspenseInfiniteQuery', () => {
7084
expectTypeOf(data).toEqualTypeOf<InfiniteData<number, unknown>>()
7185
})
7286

87+
it('should not allow skipToken in queryFn', () => {
88+
useSuspenseInfiniteQuery({
89+
queryKey: ['key'],
90+
// @ts-expect-error
91+
queryFn: skipToken,
92+
})
93+
94+
useSuspenseInfiniteQuery({
95+
queryKey: ['key'],
96+
// @ts-expect-error
97+
queryFn: Math.random() > 0.5 ? skipToken : () => Promise.resolve(5),
98+
})
99+
})
100+
73101
it('should not have pending status', () => {
74102
const { status } = useSuspenseInfiniteQuery({
75103
queryKey: ['key'],
@@ -122,14 +150,4 @@ describe('useSuspenseInfiniteQuery', () => {
122150
// @ts-expect-error TS2339
123151
query.isPlaceholderData
124152
})
125-
126-
it('should not accept skipToken type for queryFn in useSuspenseQuery', () => {
127-
const query: UseSuspenseQueryOptions = {
128-
// @ts-expect-error
129-
queryFn: skipToken,
130-
queryKey: [1],
131-
}
132-
133-
return query
134-
})
135153
})

‎packages/react-query/src/__tests__/useSuspenseQueries.test-d.tsx

+41-18
Original file line numberDiff line numberDiff line change
@@ -89,6 +89,47 @@ describe('UseSuspenseQueries config object overload', () => {
8989
expectTypeOf(data).toEqualTypeOf<{ wow: boolean }>()
9090
})
9191

92+
it('should not allow skipToken in queryFn', () => {
93+
useSuspenseQueries({
94+
queries: [
95+
{
96+
queryKey: ['key'],
97+
// @ts-expect-error
98+
queryFn: skipToken,
99+
},
100+
],
101+
})
102+
103+
useSuspenseQueries({
104+
queries: [
105+
{
106+
queryKey: ['key'],
107+
// @ts-expect-error
108+
queryFn: Math.random() > 0.5 ? skipToken : () => Promise.resolve(5),
109+
},
110+
],
111+
})
112+
})
113+
114+
it('TData should have correct type when conditional skipToken is passed', () => {
115+
const queryResults = useSuspenseQueries({
116+
queries: [
117+
{
118+
queryKey: ['withSkipToken'],
119+
// @ts-expect-error
120+
queryFn: Math.random() > 0.5 ? skipToken : () => Promise.resolve(5),
121+
},
122+
],
123+
})
124+
125+
const firstResult = queryResults[0]
126+
127+
expectTypeOf(firstResult).toEqualTypeOf<
128+
UseSuspenseQueryResult<number, Error>
129+
>()
130+
expectTypeOf(firstResult.data).toEqualTypeOf<number>()
131+
})
132+
92133
describe('custom hook', () => {
93134
it('should allow custom hooks using UseQueryOptions', () => {
94135
type Data = string
@@ -113,22 +154,4 @@ describe('UseSuspenseQueries config object overload', () => {
113154
expectTypeOf(data).toEqualTypeOf<Data>()
114155
})
115156
})
116-
117-
it('TData should have correct type when conditional skipToken is passed', () => {
118-
const queryResults = useSuspenseQueries({
119-
queries: [
120-
{
121-
queryKey: ['withSkipToken'],
122-
queryFn: Math.random() > 0.5 ? skipToken : () => Promise.resolve(5),
123-
},
124-
],
125-
})
126-
127-
const firstResult = queryResults[0]
128-
129-
expectTypeOf(firstResult).toEqualTypeOf<
130-
UseSuspenseQueryResult<number, Error>
131-
>()
132-
expectTypeOf(firstResult.data).toEqualTypeOf<number>()
133-
})
134157
})

‎packages/react-query/src/index.ts

+2
Original file line numberDiff line numberDiff line change
@@ -21,11 +21,13 @@ export { queryOptions } from './queryOptions'
2121
export type {
2222
DefinedInitialDataOptions,
2323
UndefinedInitialDataOptions,
24+
UnusedSkipTokenOptions,
2425
} from './queryOptions'
2526
export { infiniteQueryOptions } from './infiniteQueryOptions'
2627
export type {
2728
DefinedInitialDataInfiniteOptions,
2829
UndefinedInitialDataInfiniteOptions,
30+
UnusedSkipTokenInfiniteOptions,
2931
} from './infiniteQueryOptions'
3032
export {
3133
QueryClientContext,

‎packages/react-query/src/infiniteQueryOptions.ts

+56
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,9 @@ import type {
33
DefaultError,
44
InfiniteData,
55
InitialDataFunction,
6+
OmitKeyof,
67
QueryKey,
8+
SkipToken,
79
} from '@tanstack/query-core'
810
import type { UseInfiniteQueryOptions } from './types'
911

@@ -29,6 +31,36 @@ export type UndefinedInitialDataInfiniteOptions<
2931
>
3032
}
3133

34+
export type UnusedSkipTokenInfiniteOptions<
35+
TQueryFnData,
36+
TError = DefaultError,
37+
TData = InfiniteData<TQueryFnData>,
38+
TQueryKey extends QueryKey = QueryKey,
39+
TPageParam = unknown,
40+
> = OmitKeyof<
41+
UseInfiniteQueryOptions<
42+
TQueryFnData,
43+
TError,
44+
TData,
45+
TQueryFnData,
46+
TQueryKey,
47+
TPageParam
48+
>,
49+
'queryFn'
50+
> & {
51+
queryFn: Exclude<
52+
UseInfiniteQueryOptions<
53+
TQueryFnData,
54+
TError,
55+
TData,
56+
TQueryFnData,
57+
TQueryKey,
58+
TPageParam
59+
>['queryFn'],
60+
SkipToken
61+
>
62+
}
63+
3264
type NonUndefinedGuard<T> = T extends undefined ? never : T
3365

3466
export type DefinedInitialDataInfiniteOptions<
@@ -75,6 +107,30 @@ export function infiniteQueryOptions<
75107
queryKey: DataTag<TQueryKey, InfiniteData<TQueryFnData>>
76108
}
77109

110+
export function infiniteQueryOptions<
111+
TQueryFnData,
112+
TError = DefaultError,
113+
TData = InfiniteData<TQueryFnData>,
114+
TQueryKey extends QueryKey = QueryKey,
115+
TPageParam = unknown,
116+
>(
117+
options: UnusedSkipTokenInfiniteOptions<
118+
TQueryFnData,
119+
TError,
120+
TData,
121+
TQueryKey,
122+
TPageParam
123+
>,
124+
): UnusedSkipTokenInfiniteOptions<
125+
TQueryFnData,
126+
TError,
127+
TData,
128+
TQueryKey,
129+
TPageParam
130+
> & {
131+
queryKey: DataTag<TQueryKey, InfiniteData<TQueryFnData>>
132+
}
133+
78134
export function infiniteQueryOptions<
79135
TQueryFnData,
80136
TError = DefaultError,

‎packages/react-query/src/queryOptions.ts

+28
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,9 @@ import type {
22
DataTag,
33
DefaultError,
44
InitialDataFunction,
5+
OmitKeyof,
56
QueryKey,
7+
SkipToken,
68
} from '@tanstack/query-core'
79
import type { UseQueryOptions } from './types'
810

@@ -18,6 +20,21 @@ export type UndefinedInitialDataOptions<
1820
| NonUndefinedGuard<TQueryFnData>
1921
}
2022

23+
export type UnusedSkipTokenOptions<
24+
TQueryFnData = unknown,
25+
TError = DefaultError,
26+
TData = TQueryFnData,
27+
TQueryKey extends QueryKey = QueryKey,
28+
> = OmitKeyof<
29+
UseQueryOptions<TQueryFnData, TError, TData, TQueryKey>,
30+
'queryFn'
31+
> & {
32+
queryFn: Exclude<
33+
UseQueryOptions<TQueryFnData, TError, TData, TQueryKey>['queryFn'],
34+
SkipToken
35+
>
36+
}
37+
2138
type NonUndefinedGuard<T> = T extends undefined ? never : T
2239

2340
export type DefinedInitialDataOptions<
@@ -42,6 +59,17 @@ export function queryOptions<
4259
queryKey: DataTag<TQueryKey, TQueryFnData>
4360
}
4461

62+
export function queryOptions<
63+
TQueryFnData = unknown,
64+
TError = DefaultError,
65+
TData = TQueryFnData,
66+
TQueryKey extends QueryKey = QueryKey,
67+
>(
68+
options: UnusedSkipTokenOptions<TQueryFnData, TError, TData, TQueryKey>,
69+
): UnusedSkipTokenOptions<TQueryFnData, TError, TData, TQueryKey> & {
70+
queryKey: DataTag<TQueryKey, TQueryFnData>
71+
}
72+
4573
export function queryOptions<
4674
TQueryFnData = unknown,
4775
TError = DefaultError,

‎packages/react-query/src/types.ts

+22-4
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ import type {
1414
QueryKey,
1515
QueryObserverOptions,
1616
QueryObserverResult,
17+
SkipToken,
1718
} from '@tanstack/query-core'
1819

1920
export interface UseBaseQueryOptions<
@@ -47,8 +48,13 @@ export interface UseSuspenseQueryOptions<
4748
TQueryKey extends QueryKey = QueryKey,
4849
> extends OmitKeyof<
4950
UseQueryOptions<TQueryFnData, TError, TData, TQueryKey>,
50-
'enabled' | 'throwOnError' | 'placeholderData'
51-
> {}
51+
'queryFn' | 'enabled' | 'throwOnError' | 'placeholderData'
52+
> {
53+
queryFn: Exclude<
54+
UseQueryOptions<TQueryFnData, TError, TData, TQueryKey>['queryFn'],
55+
SkipToken
56+
>
57+
}
5258

5359
export interface UseInfiniteQueryOptions<
5460
TQueryFnData = unknown,
@@ -85,8 +91,20 @@ export interface UseSuspenseInfiniteQueryOptions<
8591
TQueryKey,
8692
TPageParam
8793
>,
88-
'enabled' | 'throwOnError' | 'placeholderData'
89-
> {}
94+
'queryFn' | 'enabled' | 'throwOnError' | 'placeholderData'
95+
> {
96+
queryFn: Exclude<
97+
UseInfiniteQueryOptions<
98+
TQueryFnData,
99+
TError,
100+
TData,
101+
TQueryData,
102+
TQueryKey,
103+
TPageParam
104+
>['queryFn'],
105+
SkipToken
106+
>
107+
}
90108

91109
export type UseBaseQueryResult<
92110
TData = unknown,

‎packages/react-query/src/useSuspenseInfiniteQuery.ts

+1-1
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,7 @@ export function useSuspenseInfiniteQuery<
3333
queryClient?: QueryClient,
3434
): UseSuspenseInfiniteQueryResult<TData, TError> {
3535
if (process.env.NODE_ENV !== 'production') {
36-
if (options.queryFn === skipToken) {
36+
if ((options.queryFn as any) === skipToken) {
3737
console.error('skipToken is not allowed for useSuspenseInfiniteQuery')
3838
}
3939
}

‎packages/react-query/src/useSuspenseQuery.ts

+1-1
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@ export function useSuspenseQuery<
1515
queryClient?: QueryClient,
1616
): UseSuspenseQueryResult<TData, TError> {
1717
if (process.env.NODE_ENV !== 'production') {
18-
if (options.queryFn === skipToken) {
18+
if ((options.queryFn as any) === skipToken) {
1919
console.error('skipToken is not allowed for useSuspenseQuery')
2020
}
2121
}

0 commit comments

Comments
 (0)
Please sign in to comment.