@tanstack/react-query 5.24.1 → 5.24.5

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (96) hide show
  1. package/build/codemods/coverage/clover.xml +2 -2
  2. package/build/codemods/coverage/index.html +1 -1
  3. package/build/codemods/coverage/utils/index.html +1 -1
  4. package/build/codemods/coverage/utils/index.js.html +1 -1
  5. package/build/codemods/coverage/utils/transformers/index.html +1 -1
  6. package/build/codemods/coverage/utils/transformers/query-cache-transformer.js.html +1 -1
  7. package/build/codemods/coverage/utils/transformers/query-client-transformer.js.html +1 -1
  8. package/build/codemods/coverage/utils/transformers/use-query-like-transformer.js.html +1 -1
  9. package/build/codemods/coverage/v4/index.html +1 -1
  10. package/build/codemods/coverage/v4/key-transformation.js.html +1 -1
  11. package/build/codemods/coverage/v4/replace-import-specifier.js.html +1 -1
  12. package/build/codemods/coverage/v4/utils/replacers/index.html +1 -1
  13. package/build/codemods/coverage/v4/utils/replacers/key-replacer.js.html +1 -1
  14. package/build/codemods/coverage/v5/is-loading/index.html +1 -1
  15. package/build/codemods/coverage/v5/is-loading/is-loading.js.html +1 -1
  16. package/build/codemods/coverage/v5/keep-previous-data/index.html +1 -1
  17. package/build/codemods/coverage/v5/keep-previous-data/keep-previous-data.js.html +1 -1
  18. package/build/codemods/coverage/v5/keep-previous-data/utils/already-has-placeholder-data-property.js.html +1 -1
  19. package/build/codemods/coverage/v5/keep-previous-data/utils/index.html +1 -1
  20. package/build/codemods/coverage/v5/remove-overloads/index.html +1 -1
  21. package/build/codemods/coverage/v5/remove-overloads/remove-overloads.js.html +1 -1
  22. package/build/codemods/coverage/v5/remove-overloads/transformers/filter-aware-usage-transformer.js.html +1 -1
  23. package/build/codemods/coverage/v5/remove-overloads/transformers/index.html +1 -1
  24. package/build/codemods/coverage/v5/remove-overloads/transformers/query-fn-aware-usage-transformer.js.html +1 -1
  25. package/build/codemods/coverage/v5/remove-overloads/utils/index.html +1 -1
  26. package/build/codemods/coverage/v5/remove-overloads/utils/index.js.html +1 -1
  27. package/build/codemods/coverage/v5/remove-overloads/utils/unknown-usage-error.js.html +1 -1
  28. package/build/codemods/coverage/v5/rename-hydrate/index.html +1 -1
  29. package/build/codemods/coverage/v5/rename-hydrate/rename-hydrate.js.html +1 -1
  30. package/build/codemods/coverage/v5/rename-properties/index.html +1 -1
  31. package/build/codemods/coverage/v5/rename-properties/rename-properties.js.html +1 -1
  32. package/build/legacy/suspense.cjs +1 -1
  33. package/build/legacy/suspense.cjs.map +1 -1
  34. package/build/legacy/suspense.js +1 -1
  35. package/build/legacy/suspense.js.map +1 -1
  36. package/build/legacy/useMutation.cjs +1 -3
  37. package/build/legacy/useMutation.cjs.map +1 -1
  38. package/build/legacy/useMutation.js +1 -3
  39. package/build/legacy/useMutation.js.map +1 -1
  40. package/build/legacy/useSuspenseQueries.cjs +2 -1
  41. package/build/legacy/useSuspenseQueries.cjs.map +1 -1
  42. package/build/legacy/useSuspenseQueries.js +2 -1
  43. package/build/legacy/useSuspenseQueries.js.map +1 -1
  44. package/build/legacy/useSuspenseQuery.cjs +2 -1
  45. package/build/legacy/useSuspenseQuery.cjs.map +1 -1
  46. package/build/legacy/useSuspenseQuery.js +2 -1
  47. package/build/legacy/useSuspenseQuery.js.map +1 -1
  48. package/build/legacy/utils.cjs +4 -0
  49. package/build/legacy/utils.cjs.map +1 -1
  50. package/build/legacy/utils.d.cts +2 -1
  51. package/build/legacy/utils.d.ts +2 -1
  52. package/build/legacy/utils.js +3 -0
  53. package/build/legacy/utils.js.map +1 -1
  54. package/build/modern/suspense.cjs +1 -1
  55. package/build/modern/suspense.cjs.map +1 -1
  56. package/build/modern/suspense.js +1 -1
  57. package/build/modern/suspense.js.map +1 -1
  58. package/build/modern/useMutation.cjs +1 -3
  59. package/build/modern/useMutation.cjs.map +1 -1
  60. package/build/modern/useMutation.js +1 -3
  61. package/build/modern/useMutation.js.map +1 -1
  62. package/build/modern/useSuspenseQueries.cjs +2 -1
  63. package/build/modern/useSuspenseQueries.cjs.map +1 -1
  64. package/build/modern/useSuspenseQueries.js +2 -1
  65. package/build/modern/useSuspenseQueries.js.map +1 -1
  66. package/build/modern/useSuspenseQuery.cjs +2 -1
  67. package/build/modern/useSuspenseQuery.cjs.map +1 -1
  68. package/build/modern/useSuspenseQuery.js +2 -1
  69. package/build/modern/useSuspenseQuery.js.map +1 -1
  70. package/build/modern/utils.cjs +4 -0
  71. package/build/modern/utils.cjs.map +1 -1
  72. package/build/modern/utils.d.cts +2 -1
  73. package/build/modern/utils.d.ts +2 -1
  74. package/build/modern/utils.js +3 -0
  75. package/build/modern/utils.js.map +1 -1
  76. package/package.json +2 -2
  77. package/src/__tests__/infiniteQueryOptions.test-d.tsx +142 -0
  78. package/src/__tests__/queryOptions.test-d.tsx +150 -0
  79. package/src/__tests__/suspense.test-d.tsx +124 -0
  80. package/src/__tests__/suspense.test.tsx +102 -0
  81. package/src/__tests__/useInfiniteQuery.test-d.tsx +140 -0
  82. package/src/__tests__/useMutation.test.tsx +3 -1
  83. package/src/__tests__/useQueries.test-d.tsx +126 -0
  84. package/src/__tests__/useQuery.test-d.tsx +140 -0
  85. package/src/__tests__/utils.tsx +0 -8
  86. package/src/suspense.ts +1 -1
  87. package/src/useMutation.ts +1 -3
  88. package/src/useSuspenseQueries.ts +1 -0
  89. package/src/useSuspenseQuery.ts +1 -0
  90. package/src/utils.ts +2 -0
  91. package/src/__tests__/infiniteQueryOptions.types.test.tsx +0 -177
  92. package/src/__tests__/queryOptions.types.test.tsx +0 -192
  93. package/src/__tests__/suspense.types.test.tsx +0 -147
  94. package/src/__tests__/useInfiniteQuery.type.test.tsx +0 -224
  95. package/src/__tests__/useQueries.types.test.tsx +0 -148
  96. package/src/__tests__/useQuery.types.test.tsx +0 -179
@@ -0,0 +1,150 @@
1
+ import { describe, expect, expectTypeOf, it } from 'vitest'
2
+ import { QueryClient } from '@tanstack/query-core'
3
+ import { dataTagSymbol } from '@tanstack/query-core'
4
+ import { queryOptions } from '../queryOptions'
5
+ import { useQuery } from '../useQuery'
6
+ import { useQueries } from '../useQueries'
7
+ import { useSuspenseQuery } from '../useSuspenseQuery'
8
+
9
+ describe('queryOptions', () => {
10
+ it('should not allow excess properties', () => {
11
+ queryOptions({
12
+ queryKey: ['key'],
13
+ queryFn: () => Promise.resolve(5),
14
+ // @ts-expect-error this is a good error, because stallTime does not exist!
15
+ stallTime: 1000,
16
+ })
17
+ })
18
+ it('should infer types for callbacks', () => {
19
+ queryOptions({
20
+ queryKey: ['key'],
21
+ queryFn: () => Promise.resolve(5),
22
+ staleTime: 1000,
23
+ select: (data) => {
24
+ expectTypeOf(data).toEqualTypeOf<number>()
25
+ },
26
+ })
27
+ })
28
+ it('should work when passed to useQuery', () => {
29
+ const options = queryOptions({
30
+ queryKey: ['key'],
31
+ queryFn: () => Promise.resolve(5),
32
+ })
33
+
34
+ const { data } = useQuery(options)
35
+ expectTypeOf(data).toEqualTypeOf<number | undefined>()
36
+ })
37
+ it('should work when passed to useSuspenseQuery', () => {
38
+ const options = queryOptions({
39
+ queryKey: ['key'],
40
+ queryFn: () => Promise.resolve(5),
41
+ })
42
+
43
+ const { data } = useSuspenseQuery(options)
44
+ expectTypeOf(data).toEqualTypeOf<number>()
45
+ })
46
+ it('should work when passed to fetchQuery', async () => {
47
+ const options = queryOptions({
48
+ queryKey: ['key'],
49
+ queryFn: () => Promise.resolve(5),
50
+ })
51
+
52
+ const data = await new QueryClient().fetchQuery(options)
53
+ expectTypeOf(data).toEqualTypeOf<number>()
54
+ })
55
+ it('should work when passed to useQueries', () => {
56
+ const options = queryOptions({
57
+ queryKey: ['key'],
58
+ queryFn: () => Promise.resolve(5),
59
+ })
60
+
61
+ const [{ data }] = useQueries({
62
+ queries: [options],
63
+ })
64
+
65
+ expectTypeOf(data).toEqualTypeOf<number | undefined>()
66
+ })
67
+ it('should tag the queryKey with the result type of the QueryFn', () => {
68
+ expect(() => {
69
+ const { queryKey } = queryOptions({
70
+ queryKey: ['key'],
71
+ queryFn: () => Promise.resolve(5),
72
+ })
73
+
74
+ expectTypeOf(queryKey[dataTagSymbol]).toEqualTypeOf<number>()
75
+ })
76
+ })
77
+ it('should tag the queryKey even if no promise is returned', () => {
78
+ const { queryKey } = queryOptions({
79
+ queryKey: ['key'],
80
+ queryFn: () => 5,
81
+ })
82
+
83
+ expectTypeOf(queryKey[dataTagSymbol]).toEqualTypeOf<number>()
84
+ })
85
+ it('should tag the queryKey with unknown if there is no queryFn', () => {
86
+ const { queryKey } = queryOptions({
87
+ queryKey: ['key'],
88
+ })
89
+
90
+ expectTypeOf(queryKey[dataTagSymbol]).toEqualTypeOf<unknown>()
91
+ })
92
+ it('should tag the queryKey with the result type of the QueryFn if select is used', () => {
93
+ const { queryKey } = queryOptions({
94
+ queryKey: ['key'],
95
+ queryFn: () => Promise.resolve(5),
96
+ select: (data) => data.toString(),
97
+ })
98
+
99
+ expectTypeOf(queryKey[dataTagSymbol]).toEqualTypeOf<number>()
100
+ })
101
+ it('should return the proper type when passed to getQueryData', () => {
102
+ const { queryKey } = queryOptions({
103
+ queryKey: ['key'],
104
+ queryFn: () => Promise.resolve(5),
105
+ })
106
+
107
+ const queryClient = new QueryClient()
108
+ const data = queryClient.getQueryData(queryKey)
109
+ expectTypeOf(data).toEqualTypeOf<number | undefined>()
110
+ })
111
+ it('should return the proper type when passed to getQueryState', () => {
112
+ const { queryKey } = queryOptions({
113
+ queryKey: ['key'],
114
+ queryFn: () => Promise.resolve(5),
115
+ })
116
+
117
+ const queryClient = new QueryClient()
118
+ const state = queryClient.getQueryState(queryKey)
119
+ expectTypeOf(state?.data).toEqualTypeOf<number | undefined>()
120
+ })
121
+ it('should properly type updaterFn when passed to setQueryData', () => {
122
+ const { queryKey } = queryOptions({
123
+ queryKey: ['key'],
124
+ queryFn: () => Promise.resolve(5),
125
+ })
126
+
127
+ const queryClient = new QueryClient()
128
+ const data = queryClient.setQueryData(queryKey, (prev) => {
129
+ expectTypeOf(prev).toEqualTypeOf<number | undefined>()
130
+ return prev
131
+ })
132
+ expectTypeOf(data).toEqualTypeOf<number | undefined>()
133
+ })
134
+ it('should properly type value when passed to setQueryData', () => {
135
+ const { queryKey } = queryOptions({
136
+ queryKey: ['key'],
137
+ queryFn: () => Promise.resolve(5),
138
+ })
139
+
140
+ const queryClient = new QueryClient()
141
+
142
+ // @ts-expect-error value should be a number
143
+ queryClient.setQueryData(queryKey, '5')
144
+ // @ts-expect-error value should be a number
145
+ queryClient.setQueryData(queryKey, () => '5')
146
+
147
+ const data = queryClient.setQueryData(queryKey, 5)
148
+ expectTypeOf(data).toEqualTypeOf<number | undefined>()
149
+ })
150
+ })
@@ -0,0 +1,124 @@
1
+ import { describe, expectTypeOf, it } from 'vitest'
2
+ import { useSuspenseQuery } from '../useSuspenseQuery'
3
+ import { useSuspenseInfiniteQuery } from '../useSuspenseInfiniteQuery'
4
+ import type { InfiniteData } from '@tanstack/query-core'
5
+
6
+ describe('useSuspenseQuery', () => {
7
+ it('should always have data defined', () => {
8
+ const { data } = useSuspenseQuery({
9
+ queryKey: ['key'],
10
+ queryFn: () => Promise.resolve(5),
11
+ })
12
+
13
+ expectTypeOf(data).toEqualTypeOf<number>()
14
+ })
15
+
16
+ it('should not have pending status', () => {
17
+ const { status } = useSuspenseQuery({
18
+ queryKey: ['key'],
19
+ queryFn: () => Promise.resolve(5),
20
+ })
21
+
22
+ expectTypeOf(status).toEqualTypeOf<'error' | 'success'>()
23
+ })
24
+
25
+ it('should not allow placeholderData, enabled or throwOnError props', () => {
26
+ useSuspenseQuery({
27
+ queryKey: ['key'],
28
+ queryFn: () => Promise.resolve(5),
29
+ // @ts-expect-error TS2345
30
+ placeholderData: 5,
31
+ enabled: true,
32
+ })
33
+
34
+ useSuspenseQuery({
35
+ queryKey: ['key'],
36
+ queryFn: () => Promise.resolve(5),
37
+ // @ts-expect-error TS2345
38
+ enabled: true,
39
+ })
40
+
41
+ useSuspenseQuery({
42
+ queryKey: ['key'],
43
+ queryFn: () => Promise.resolve(5),
44
+ // @ts-expect-error TS2345
45
+ throwOnError: true,
46
+ })
47
+ })
48
+
49
+ it('should not return isPlaceholderData', () => {
50
+ const query = useSuspenseQuery({
51
+ queryKey: ['key'],
52
+ queryFn: () => Promise.resolve(5),
53
+ })
54
+
55
+ // @ts-expect-error TS2339
56
+ query.isPlaceholderData
57
+ })
58
+ })
59
+
60
+ describe('useSuspenseInfiniteQuery', () => {
61
+ it('should always have data defined', () => {
62
+ const { data } = useSuspenseInfiniteQuery({
63
+ queryKey: ['key'],
64
+ queryFn: () => Promise.resolve(5),
65
+ initialPageParam: 1,
66
+ getNextPageParam: () => 1,
67
+ })
68
+
69
+ expectTypeOf(data).toEqualTypeOf<InfiniteData<number, unknown>>()
70
+ })
71
+
72
+ it('should not have pending status', () => {
73
+ const { status } = useSuspenseInfiniteQuery({
74
+ queryKey: ['key'],
75
+ queryFn: () => Promise.resolve(5),
76
+ initialPageParam: 1,
77
+ getNextPageParam: () => 1,
78
+ })
79
+
80
+ expectTypeOf(status).toEqualTypeOf<'error' | 'success'>()
81
+ })
82
+
83
+ it('should not allow placeholderData, enabled or throwOnError props', () => {
84
+ useSuspenseInfiniteQuery({
85
+ queryKey: ['key'],
86
+ queryFn: () => Promise.resolve(5),
87
+ initialPageParam: 1,
88
+ getNextPageParam: () => 1,
89
+ // @ts-expect-error TS2345
90
+ placeholderData: 5,
91
+ enabled: true,
92
+ })
93
+
94
+ useSuspenseInfiniteQuery({
95
+ queryKey: ['key'],
96
+ queryFn: () => Promise.resolve(5),
97
+ initialPageParam: 1,
98
+ getNextPageParam: () => 1,
99
+ // @ts-expect-error TS2345
100
+ enabled: true,
101
+ })
102
+
103
+ useSuspenseInfiniteQuery({
104
+ queryKey: ['key'],
105
+ queryFn: () => Promise.resolve(5),
106
+ initialPageParam: 1,
107
+ getNextPageParam: () => 1,
108
+ // @ts-expect-error TS2345
109
+ throwOnError: true,
110
+ })
111
+ })
112
+
113
+ it('should not return isPlaceholderData', () => {
114
+ const query = useSuspenseInfiniteQuery({
115
+ queryKey: ['key'],
116
+ queryFn: () => Promise.resolve(5),
117
+ initialPageParam: 1,
118
+ getNextPageParam: () => 1,
119
+ })
120
+
121
+ // @ts-expect-error TS2339
122
+ query.isPlaceholderData
123
+ })
124
+ })
@@ -779,6 +779,58 @@ describe('useSuspenseQuery', () => {
779
779
 
780
780
  consoleMock.mockRestore()
781
781
  })
782
+
783
+ it('should still suspense if queryClient has placeholderData config', async () => {
784
+ const key = queryKey()
785
+ const queryClientWithPlaceholder = createQueryClient({
786
+ defaultOptions: {
787
+ queries: {
788
+ placeholderData: (previousData: any) => previousData,
789
+ },
790
+ },
791
+ })
792
+ const states: Array<UseSuspenseQueryResult<number>> = []
793
+
794
+ let count = 0
795
+ let renders = 0
796
+
797
+ function Page() {
798
+ renders++
799
+
800
+ const [stateKey, setStateKey] = React.useState(key)
801
+
802
+ const state = useSuspenseQuery({
803
+ queryKey: stateKey,
804
+ queryFn: async () => {
805
+ count++
806
+ await sleep(100)
807
+ return count
808
+ },
809
+ })
810
+
811
+ states.push(state)
812
+
813
+ return (
814
+ <div>
815
+ <button aria-label="toggle" onClick={() => setStateKey(queryKey())} />
816
+ data: {String(state.data)}
817
+ </div>
818
+ )
819
+ }
820
+
821
+ const rendered = renderWithClient(
822
+ queryClientWithPlaceholder,
823
+ <React.Suspense fallback="loading">
824
+ <Page />
825
+ </React.Suspense>,
826
+ )
827
+ await waitFor(() => rendered.getByText('loading'))
828
+ await waitFor(() => rendered.getByText('data: 1'))
829
+ fireEvent.click(rendered.getByLabelText('toggle'))
830
+
831
+ await waitFor(() => rendered.getByText('loading'))
832
+ await waitFor(() => rendered.getByText('data: 2'))
833
+ })
782
834
  })
783
835
 
784
836
  describe('useSuspenseQueries', () => {
@@ -1090,4 +1142,54 @@ describe('useSuspenseQueries', () => {
1090
1142
 
1091
1143
  expect(queryFnCount).toBe(2)
1092
1144
  })
1145
+
1146
+ it('should still suspense if queryClient has placeholderData config', async () => {
1147
+ const key = queryKey()
1148
+ const queryClientWithPlaceholder = createQueryClient({
1149
+ defaultOptions: {
1150
+ queries: {
1151
+ placeholderData: (previousData: any) => previousData,
1152
+ },
1153
+ },
1154
+ })
1155
+
1156
+ function Page() {
1157
+ const [count, setCount] = React.useState(0)
1158
+ const [isPending, startTransition] = React.useTransition()
1159
+ const { data } = useSuspenseQuery({
1160
+ queryKey: [key, count],
1161
+ queryFn: async () => {
1162
+ await sleep(10)
1163
+ return 'data' + count
1164
+ },
1165
+ })
1166
+
1167
+ return (
1168
+ <div>
1169
+ <button onClick={() => startTransition(() => setCount(count + 1))}>
1170
+ inc
1171
+ </button>
1172
+
1173
+ <div>{isPending ? 'Pending...' : String(data)}</div>
1174
+ </div>
1175
+ )
1176
+ }
1177
+
1178
+ const rendered = renderWithClient(
1179
+ queryClientWithPlaceholder,
1180
+ <React.Suspense fallback={'Loading...'}>
1181
+ <Page />
1182
+ </React.Suspense>,
1183
+ )
1184
+
1185
+ await waitFor(() => rendered.getByText('Loading...'))
1186
+
1187
+ await waitFor(() => rendered.getByText('data0'))
1188
+
1189
+ fireEvent.click(rendered.getByText('inc'))
1190
+
1191
+ await waitFor(() => rendered.getByText('Pending...'))
1192
+
1193
+ await waitFor(() => rendered.getByText('data1'))
1194
+ })
1093
1195
  })
@@ -0,0 +1,140 @@
1
+ import { describe, expectTypeOf, it } from 'vitest'
2
+ import { QueryClient } from '@tanstack/query-core'
3
+ import { useInfiniteQuery } from '../useInfiniteQuery'
4
+ import { useQuery } from '../useQuery'
5
+ import type { InfiniteData } from '@tanstack/query-core'
6
+
7
+ describe('pageParam', () => {
8
+ it('initialPageParam should define type of param passed to queryFunctionContext', () => {
9
+ useInfiniteQuery({
10
+ queryKey: ['key'],
11
+ queryFn: ({ pageParam }) => {
12
+ expectTypeOf(pageParam).toEqualTypeOf<number>()
13
+ },
14
+ initialPageParam: 1,
15
+ getNextPageParam: () => undefined,
16
+ })
17
+ })
18
+
19
+ it('direction should be passed to queryFn of useInfiniteQuery', () => {
20
+ useInfiniteQuery({
21
+ queryKey: ['key'],
22
+ queryFn: ({ direction }) => {
23
+ expectTypeOf(direction).toEqualTypeOf<'forward' | 'backward'>()
24
+ },
25
+ initialPageParam: 1,
26
+ getNextPageParam: () => undefined,
27
+ })
28
+ })
29
+
30
+ it('there should be no pageParam passed to the queryFn of useQuery', () => {
31
+ useQuery({
32
+ queryKey: ['key'],
33
+ // @ts-expect-error there should be no pageParam passed to queryFn of useQuery
34
+ queryFn: ({ pageParam }) => {
35
+ return String(pageParam)
36
+ },
37
+ })
38
+ })
39
+
40
+ it('there should be no direction passed to the queryFn of useQuery', () => {
41
+ useQuery({
42
+ queryKey: ['key'],
43
+ // @ts-expect-error there should be no pageParam passed to queryFn of useQuery
44
+ queryFn: ({ direction }) => {
45
+ return String(direction)
46
+ },
47
+ })
48
+ })
49
+
50
+ it('initialPageParam should define type of param passed to queryFunctionContext for fetchInfiniteQuery', () => {
51
+ const queryClient = new QueryClient()
52
+ queryClient.fetchInfiniteQuery({
53
+ queryKey: ['key'],
54
+ queryFn: ({ pageParam }) => {
55
+ expectTypeOf(pageParam).toEqualTypeOf<number>()
56
+ },
57
+ initialPageParam: 1,
58
+ })
59
+ })
60
+
61
+ it('initialPageParam should define type of param passed to queryFunctionContext for prefetchInfiniteQuery', () => {
62
+ const queryClient = new QueryClient()
63
+ queryClient.prefetchInfiniteQuery({
64
+ queryKey: ['key'],
65
+ queryFn: ({ pageParam }) => {
66
+ expectTypeOf(pageParam).toEqualTypeOf<number>()
67
+ },
68
+ initialPageParam: 1,
69
+ })
70
+ })
71
+ })
72
+ describe('select', () => {
73
+ it('should still return paginated data if no select result', () => {
74
+ const infiniteQuery = useInfiniteQuery({
75
+ queryKey: ['key'],
76
+ queryFn: ({ pageParam }) => {
77
+ return pageParam * 5
78
+ },
79
+ initialPageParam: 1,
80
+ getNextPageParam: () => undefined,
81
+ })
82
+
83
+ // TODO: Order of generics prevents pageParams to be typed correctly. Using `unknown` for now
84
+ expectTypeOf(infiniteQuery.data).toEqualTypeOf<
85
+ InfiniteData<number, unknown> | undefined
86
+ >()
87
+ })
88
+
89
+ it('should be able to transform data to arbitrary result', () => {
90
+ const infiniteQuery = useInfiniteQuery({
91
+ queryKey: ['key'],
92
+ queryFn: ({ pageParam }) => {
93
+ return pageParam * 5
94
+ },
95
+ initialPageParam: 1,
96
+ getNextPageParam: () => undefined,
97
+ select: (data) => {
98
+ expectTypeOf(data).toEqualTypeOf<InfiniteData<number, number>>()
99
+ return 'selected' as const
100
+ },
101
+ })
102
+
103
+ expectTypeOf(infiniteQuery.data).toEqualTypeOf<'selected' | undefined>()
104
+ })
105
+ })
106
+ describe('getNextPageParam / getPreviousPageParam', () => {
107
+ it('should get typed params', () => {
108
+ const infiniteQuery = useInfiniteQuery({
109
+ queryKey: ['key'],
110
+ queryFn: ({ pageParam }) => {
111
+ return String(pageParam)
112
+ },
113
+ initialPageParam: 1,
114
+ getNextPageParam: (lastPage, allPages, lastPageParam, allPageParams) => {
115
+ expectTypeOf(lastPage).toEqualTypeOf<string>()
116
+ expectTypeOf(allPages).toEqualTypeOf<Array<string>>()
117
+ expectTypeOf(lastPageParam).toEqualTypeOf<number>()
118
+ expectTypeOf(allPageParams).toEqualTypeOf<Array<number>>()
119
+ return undefined
120
+ },
121
+ getPreviousPageParam: (
122
+ firstPage,
123
+ allPages,
124
+ firstPageParam,
125
+ allPageParams,
126
+ ) => {
127
+ expectTypeOf(firstPage).toEqualTypeOf<string>()
128
+ expectTypeOf(allPages).toEqualTypeOf<Array<string>>()
129
+ expectTypeOf(firstPageParam).toEqualTypeOf<number>()
130
+ expectTypeOf(allPageParams).toEqualTypeOf<Array<number>>()
131
+ return undefined
132
+ },
133
+ })
134
+
135
+ // TODO: Order of generics prevents pageParams to be typed correctly. Using `unknown` for now
136
+ expectTypeOf(infiniteQuery.data).toEqualTypeOf<
137
+ InfiniteData<string, unknown> | undefined
138
+ >()
139
+ })
140
+ })
@@ -1138,6 +1138,8 @@ describe('useMutation', () => {
1138
1138
 
1139
1139
  fireEvent.click(rendered.getByRole('button', { name: /mutate/i }))
1140
1140
 
1141
- await rendered.findByText('data: custom client, status: success')
1141
+ await waitFor(() =>
1142
+ rendered.findByText('data: custom client, status: success'),
1143
+ )
1142
1144
  })
1143
1145
  })
@@ -0,0 +1,126 @@
1
+ import { describe, expectTypeOf, it } from 'vitest'
2
+ import { queryOptions } from '../queryOptions'
3
+ import { useQueries } from '../useQueries'
4
+ import type { UseQueryOptions } from '../types'
5
+
6
+ describe('UseQueries config object overload', () => {
7
+ it('TData should always be defined when initialData is provided as an object', () => {
8
+ const query1 = {
9
+ queryKey: ['key1'],
10
+ queryFn: () => {
11
+ return {
12
+ wow: true,
13
+ }
14
+ },
15
+ initialData: {
16
+ wow: false,
17
+ },
18
+ }
19
+
20
+ const query2 = {
21
+ queryKey: ['key2'],
22
+ queryFn: () => 'Query Data',
23
+ initialData: 'initial data',
24
+ }
25
+
26
+ const query3 = {
27
+ queryKey: ['key2'],
28
+ queryFn: () => 'Query Data',
29
+ }
30
+
31
+ const queryResults = useQueries({ queries: [query1, query2, query3] })
32
+
33
+ const query1Data = queryResults[0].data
34
+ const query2Data = queryResults[1].data
35
+ const query3Data = queryResults[2].data
36
+
37
+ expectTypeOf(query1Data).toEqualTypeOf<{ wow: boolean }>()
38
+ expectTypeOf(query2Data).toEqualTypeOf<string>()
39
+ expectTypeOf(query3Data).toEqualTypeOf<string | undefined>()
40
+ })
41
+
42
+ it('TData should be defined when passed through queryOptions', () => {
43
+ const options = queryOptions({
44
+ queryKey: ['key'],
45
+ queryFn: () => {
46
+ return {
47
+ wow: true,
48
+ }
49
+ },
50
+ initialData: {
51
+ wow: true,
52
+ },
53
+ })
54
+ const queryResults = useQueries({ queries: [options] })
55
+
56
+ const data = queryResults[0].data
57
+
58
+ expectTypeOf(data).toEqualTypeOf<{ wow: boolean }>()
59
+ })
60
+
61
+ it('it should be possible to define a different TData than TQueryFnData using select with queryOptions spread into useQuery', () => {
62
+ const query1 = queryOptions({
63
+ queryKey: ['key'],
64
+ queryFn: () => Promise.resolve(1),
65
+ select: (data) => data > 1,
66
+ })
67
+
68
+ const query2 = {
69
+ queryKey: ['key'],
70
+ queryFn: () => Promise.resolve(1),
71
+ select: (data: number) => data > 1,
72
+ }
73
+
74
+ const queryResults = useQueries({ queries: [query1, query2] })
75
+ const query1Data = queryResults[0].data
76
+ const query2Data = queryResults[1].data
77
+
78
+ expectTypeOf(query1Data).toEqualTypeOf<boolean | undefined>()
79
+ expectTypeOf(query2Data).toEqualTypeOf<boolean | undefined>()
80
+ })
81
+
82
+ it('TData should have undefined in the union when initialData is provided as a function which can return undefined', () => {
83
+ const queryResults = useQueries({
84
+ queries: [
85
+ {
86
+ queryKey: ['key'],
87
+ queryFn: () => {
88
+ return {
89
+ wow: true,
90
+ }
91
+ },
92
+ initialData: () => undefined as { wow: boolean } | undefined,
93
+ },
94
+ ],
95
+ })
96
+
97
+ const data = queryResults[0].data
98
+
99
+ expectTypeOf(data).toEqualTypeOf<{ wow: boolean } | undefined>()
100
+ })
101
+
102
+ describe('custom hook', () => {
103
+ it('should allow custom hooks using UseQueryOptions', () => {
104
+ type Data = string
105
+
106
+ const useCustomQueries = (
107
+ options?: Omit<UseQueryOptions<Data>, 'queryKey' | 'queryFn'>,
108
+ ) => {
109
+ return useQueries({
110
+ queries: [
111
+ {
112
+ ...options,
113
+ queryKey: ['todos-key'],
114
+ queryFn: () => Promise.resolve('data'),
115
+ },
116
+ ],
117
+ })
118
+ }
119
+
120
+ const queryResults = useCustomQueries()
121
+ const data = queryResults[0].data
122
+
123
+ expectTypeOf(data).toEqualTypeOf<Data | undefined>()
124
+ })
125
+ })
126
+ })