Skip to content

Commit f63ba16

Browse files
gs18004TkDodo
andauthored
fix(types): prevent type errors and improve inference for dynamic queries on useQueries and useSuspenseQueries (#8624)
* fix(react-query): prevent type errors and improve inference for dynamic queries on useQueries and useSuspenseQueries Previously, using useQueries and useSuspenseQueries with a dynamic array of mixed queries caused type errors. This commit ensures that type errors no longer occur and that the returned data is correctly inferred (e.g., as (number | boolean | undefined)[]) instead of unknown[]. * fix(react-query): improve type inference for useQueries and useSuspenseQueries results * fix(react-query): fix type of queries in useQueries and useSuspenseQueries * refactor: remove meaningless depths * test(react-query): move type only tests * test(react-query): fix type test error * fix(vue-query): prevent type errors and improve inference for dynamicqueries on useQueries * fix: prevent type errors and improve inference for dynamicqueries * fix: solid type tests not sure why, but compilation must run first with the current tsc version in case solid-query isn't built yet * fix: prevent type errors and improve inference for dynamic queries * chore: remove test * test(react-query): fix failing type tests * fix: fix test syntax error * test(react-query): remove tests for result[0].data * fix: change the result fallback to fix test fail * rollback(angular-query-experimental): restore injectQueries --------- Co-authored-by: Dominik Dorfmeister <office@dorfmeister.cc>
1 parent a4db9ed commit f63ba16

File tree

13 files changed

+190
-94
lines changed

13 files changed

+190
-94
lines changed

packages/angular-query-experimental/src/inject-queries.ts

+4
Original file line numberDiff line numberDiff line change
@@ -193,6 +193,10 @@ export type QueriesResults<
193193
Array<QueryObserverResult>
194194

195195
/**
196+
* @param root0
197+
* @param root0.queries
198+
* @param root0.combine
199+
* @param injector
196200
* @public
197201
*/
198202
export function injectQueries<

packages/react-query/src/__tests__/useQueries.test-d.tsx

+26
Original file line numberDiff line numberDiff line change
@@ -141,4 +141,30 @@ describe('UseQueries config object overload', () => {
141141
expectTypeOf(firstResult).toEqualTypeOf<UseQueryResult<number, Error>>()
142142
expectTypeOf(firstResult.data).toEqualTypeOf<number | undefined>()
143143
})
144+
145+
it('should return correct data for dynamic queries with mixed result types', () => {
146+
const Queries1 = {
147+
get: () =>
148+
queryOptions({
149+
queryKey: ['key1'],
150+
queryFn: () => Promise.resolve(1),
151+
}),
152+
}
153+
const Queries2 = {
154+
get: () =>
155+
queryOptions({
156+
queryKey: ['key2'],
157+
queryFn: () => Promise.resolve(true),
158+
}),
159+
}
160+
161+
const queries1List = [1, 2, 3].map(() => ({ ...Queries1.get() }))
162+
const result = useQueries({
163+
queries: [...queries1List, { ...Queries2.get() }],
164+
})
165+
166+
expectTypeOf(result).toEqualTypeOf<
167+
[...Array<UseQueryResult<number, Error>>, UseQueryResult<boolean, Error>]
168+
>()
169+
})
144170
})

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

+29
Original file line numberDiff line numberDiff line change
@@ -155,6 +155,35 @@ describe('UseSuspenseQueries config object overload', () => {
155155
})
156156
})
157157

158+
it('should return correct data for dynamic queries with mixed result types', () => {
159+
const Queries1 = {
160+
get: () =>
161+
queryOptions({
162+
queryKey: ['key1'],
163+
queryFn: () => Promise.resolve(1),
164+
}),
165+
}
166+
const Queries2 = {
167+
get: () =>
168+
queryOptions({
169+
queryKey: ['key2'],
170+
queryFn: () => Promise.resolve(true),
171+
}),
172+
}
173+
174+
const queries1List = [1, 2, 3].map(() => ({ ...Queries1.get() }))
175+
const result = useSuspenseQueries({
176+
queries: [...queries1List, { ...Queries2.get() }],
177+
})
178+
179+
expectTypeOf(result).toEqualTypeOf<
180+
[
181+
...Array<UseSuspenseQueryResult<number, Error>>,
182+
UseSuspenseQueryResult<boolean, Error>,
183+
]
184+
>()
185+
})
186+
158187
it('queryOptions with initialData works on useSuspenseQueries', () => {
159188
const query1 = queryOptions({
160189
queryKey: ['key1'],

packages/react-query/src/useQueries.ts

+4-18
Original file line numberDiff line numberDiff line change
@@ -203,23 +203,7 @@ export type QueriesResults<
203203
[...TResults, GetUseQueryResult<Head>],
204204
[...TDepth, 1]
205205
>
206-
: T extends Array<
207-
UseQueryOptionsForUseQueries<
208-
infer TQueryFnData,
209-
infer TError,
210-
infer TData,
211-
any
212-
>
213-
>
214-
? // Dynamic-size (homogenous) UseQueryOptions array: map directly to array of results
215-
Array<
216-
UseQueryResult<
217-
unknown extends TData ? TQueryFnData : TData,
218-
unknown extends TError ? DefaultError : TError
219-
>
220-
>
221-
: // Fallback
222-
Array<UseQueryResult>
206+
: { [K in keyof T]: GetUseQueryResult<T[K]> }
223207

224208
export function useQueries<
225209
T extends Array<any>,
@@ -229,7 +213,9 @@ export function useQueries<
229213
queries,
230214
...options
231215
}: {
232-
queries: readonly [...QueriesOptions<T>]
216+
queries:
217+
| readonly [...QueriesOptions<T>]
218+
| readonly [...{ [K in keyof T]: GetUseQueryOptionsForUseQueries<T[K]> }]
233219
combine?: (result: QueriesResults<T>) => TCombinedResult
234220
subscribed?: boolean
235221
},

packages/react-query/src/useSuspenseQueries.ts

+4-18
Original file line numberDiff line numberDiff line change
@@ -160,30 +160,16 @@ export type SuspenseQueriesResults<
160160
[...TResults, GetUseSuspenseQueryResult<Head>],
161161
[...TDepth, 1]
162162
>
163-
: T extends Array<
164-
UseSuspenseQueryOptions<
165-
infer TQueryFnData,
166-
infer TError,
167-
infer TData,
168-
any
169-
>
170-
>
171-
? // Dynamic-size (homogenous) UseQueryOptions array: map directly to array of results
172-
Array<
173-
UseSuspenseQueryResult<
174-
unknown extends TData ? TQueryFnData : TData,
175-
unknown extends TError ? DefaultError : TError
176-
>
177-
>
178-
: // Fallback
179-
Array<UseSuspenseQueryResult>
163+
: { [K in keyof T]: GetUseSuspenseQueryResult<T[K]> }
180164

181165
export function useSuspenseQueries<
182166
T extends Array<any>,
183167
TCombinedResult = SuspenseQueriesResults<T>,
184168
>(
185169
options: {
186-
queries: readonly [...SuspenseQueriesOptions<T>]
170+
queries:
171+
| readonly [...SuspenseQueriesOptions<T>]
172+
| readonly [...{ [K in keyof T]: GetUseSuspenseQueryOptions<T[K]> }]
187173
combine?: (result: SuspenseQueriesResults<T>) => TCombinedResult
188174
},
189175
queryClient?: QueryClient,

packages/solid-query-devtools/package.json

+1-2
Original file line numberDiff line numberDiff line change
@@ -18,15 +18,14 @@
1818
"clean": "premove ./build ./coverage ./dist-ts",
1919
"compile": "tsc --build",
2020
"test:eslint": "eslint ./src",
21-
"test:types": "npm-run-all --serial test:types:*",
21+
"test:types": "npm run compile && npm-run-all --serial test:types:*",
2222
"test:types:ts50": "node ../../node_modules/typescript50/lib/tsc.js --build",
2323
"test:types:ts51": "node ../../node_modules/typescript51/lib/tsc.js --build",
2424
"test:types:ts52": "node ../../node_modules/typescript52/lib/tsc.js --build",
2525
"test:types:ts53": "node ../../node_modules/typescript53/lib/tsc.js --build",
2626
"test:types:ts54": "node ../../node_modules/typescript54/lib/tsc.js --build",
2727
"test:types:ts55": "node ../../node_modules/typescript55/lib/tsc.js --build",
2828
"test:types:ts56": "node ../../node_modules/typescript56/lib/tsc.js --build",
29-
"test:types:ts57": "tsc --build",
3029
"test:build": "publint --strict && attw --pack",
3130
"build": "tsup --tsconfig tsconfig.prod.json",
3231
"build:dev": "tsup --watch"

packages/solid-query-persist-client/package.json

+1-2
Original file line numberDiff line numberDiff line change
@@ -18,15 +18,14 @@
1818
"clean": "premove ./build ./coverage ./dist-ts",
1919
"compile": "tsc --build",
2020
"test:eslint": "eslint ./src",
21-
"test:types": "npm-run-all --serial test:types:*",
21+
"test:types": "npm run compile && npm-run-all --serial test:types:*",
2222
"test:types:ts50": "node ../../node_modules/typescript50/lib/tsc.js --build",
2323
"test:types:ts51": "node ../../node_modules/typescript51/lib/tsc.js --build",
2424
"test:types:ts52": "node ../../node_modules/typescript52/lib/tsc.js --build",
2525
"test:types:ts53": "node ../../node_modules/typescript53/lib/tsc.js --build",
2626
"test:types:ts54": "node ../../node_modules/typescript54/lib/tsc.js --build",
2727
"test:types:ts55": "node ../../node_modules/typescript55/lib/tsc.js --build",
2828
"test:types:ts56": "node ../../node_modules/typescript56/lib/tsc.js --build",
29-
"test:types:ts57": "tsc --build",
3029
"test:lib": "vitest",
3130
"test:lib:dev": "pnpm run test:lib --watch",
3231
"test:build": "publint --strict && attw --pack",
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
import { describe, expectTypeOf, it } from 'vitest'
2+
import { createQueries, queryOptions } from '..'
3+
import type { CreateQueryResult } from '..'
4+
5+
describe('createQueries', () => {
6+
it('should return correct data for dynamic queries with mixed result types', () => {
7+
const Queries1 = {
8+
get: () =>
9+
queryOptions({
10+
queryKey: ['key1'],
11+
queryFn: () => Promise.resolve(1),
12+
}),
13+
}
14+
const Queries2 = {
15+
get: () =>
16+
queryOptions({
17+
queryKey: ['key2'],
18+
queryFn: () => Promise.resolve(true),
19+
}),
20+
}
21+
22+
const queries1List = [1, 2, 3].map(() => ({ ...Queries1.get() }))
23+
const result = createQueries(() => ({
24+
queries: [...queries1List, { ...Queries2.get() }],
25+
}))
26+
27+
expectTypeOf(result).toEqualTypeOf<
28+
[
29+
...Array<CreateQueryResult<number, Error>>,
30+
CreateQueryResult<boolean, Error>,
31+
]
32+
>()
33+
})
34+
})

packages/solid-query/src/createQueries.ts

+4-18
Original file line numberDiff line numberDiff line change
@@ -183,30 +183,16 @@ type QueriesResults<
183183
[...TResult, GetResults<Head>],
184184
[...TDepth, 1]
185185
>
186-
: T extends Array<
187-
CreateQueryOptionsForCreateQueries<
188-
infer TQueryFnData,
189-
infer TError,
190-
infer TData,
191-
any
192-
>
193-
>
194-
? // Dynamic-size (homogenous) UseQueryOptions array: map directly to array of results
195-
Array<
196-
CreateQueryResult<
197-
unknown extends TData ? TQueryFnData : TData,
198-
unknown extends TError ? DefaultError : TError
199-
>
200-
>
201-
: // Fallback
202-
Array<CreateQueryResult>
186+
: { [K in keyof T]: GetResults<T[K]> }
203187

204188
export function createQueries<
205189
T extends Array<any>,
206190
TCombinedResult extends QueriesResults<T> = QueriesResults<T>,
207191
>(
208192
queriesOptions: Accessor<{
209-
queries: readonly [...QueriesOptions<T>]
193+
queries:
194+
| readonly [...QueriesOptions<T>]
195+
| readonly [...{ [K in keyof T]: GetOptions<T[K]> }]
210196
combine?: (result: QueriesResults<T>) => TCombinedResult
211197
}>,
212198
queryClient?: Accessor<QueryClient>,

packages/svelte-query/src/createQueries.ts

+6-18
Original file line numberDiff line numberDiff line change
@@ -184,23 +184,7 @@ export type QueriesResults<
184184
[...TResults, GetCreateQueryResult<Head>],
185185
[...TDepth, 1]
186186
>
187-
: T extends Array<
188-
QueryObserverOptionsForCreateQueries<
189-
infer TQueryFnData,
190-
infer TError,
191-
infer TData,
192-
any
193-
>
194-
>
195-
? // Dynamic-size (homogenous) CreateQueryOptions array: map directly to array of results
196-
Array<
197-
QueryObserverResult<
198-
unknown extends TData ? TQueryFnData : TData,
199-
unknown extends TError ? DefaultError : TError
200-
>
201-
>
202-
: // Fallback
203-
Array<QueryObserverResult>
187+
: { [K in keyof T]: GetCreateQueryResult<T[K]> }
204188

205189
export function createQueries<
206190
T extends Array<any>,
@@ -210,7 +194,11 @@ export function createQueries<
210194
queries,
211195
...options
212196
}: {
213-
queries: StoreOrVal<[...QueriesOptions<T>]>
197+
queries:
198+
| StoreOrVal<[...QueriesOptions<T>]>
199+
| StoreOrVal<
200+
[...{ [K in keyof T]: GetQueryObserverOptionsForCreateQueries<T[K]> }]
201+
>
214202
combine?: (result: QueriesResults<T>) => TCombinedResult
215203
},
216204
queryClient?: QueryClient,

packages/svelte-query/tests/createQueries/createQueries.test-d.ts

+36
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ import { describe, expectTypeOf, test } from 'vitest'
22
import { get } from 'svelte/store'
33
import { skipToken } from '@tanstack/query-core'
44
import { createQueries, queryOptions } from '../../src/index.js'
5+
import type { Readable } from 'svelte/store'
56
import type { OmitKeyof, QueryObserverResult } from '@tanstack/query-core'
67
import type { CreateQueryOptions } from '../../src/index.js'
78

@@ -65,4 +66,39 @@ describe('createQueries', () => {
6566
>()
6667
expectTypeOf(firstResult.data).toEqualTypeOf<number | undefined>()
6768
})
69+
70+
test('should return correct data for dynamic queries with mixed result types', () => {
71+
const Queries1 = {
72+
get: () =>
73+
queryOptions({
74+
queryKey: ['key1'],
75+
queryFn: () => Promise.resolve(1),
76+
}),
77+
}
78+
const Queries2 = {
79+
get: () =>
80+
queryOptions({
81+
queryKey: ['key2'],
82+
queryFn: () => Promise.resolve(true),
83+
}),
84+
}
85+
86+
const queries1List = [1, 2, 3].map(() => ({ ...Queries1.get() }))
87+
const result = createQueries({
88+
queries: [...queries1List, { ...Queries2.get() }],
89+
})
90+
91+
expectTypeOf(result).toEqualTypeOf<
92+
Readable<
93+
[
94+
...Array<QueryObserverResult<number, Error>>,
95+
QueryObserverResult<boolean, Error>,
96+
]
97+
>
98+
>()
99+
100+
expectTypeOf(get(result)[0].data).toEqualTypeOf<
101+
number | boolean | undefined
102+
>()
103+
})
68104
})

packages/vue-query/src/__tests__/useQueries.test-d.ts

+33
Original file line numberDiff line numberDiff line change
@@ -219,4 +219,37 @@ describe('UseQueries config object overload', () => {
219219

220220
expectTypeOf(queryCombineWithoutSelect.value).toEqualTypeOf<Array<number>>()
221221
})
222+
223+
it('should return correct data for dynamic queries with mixed result types', () => {
224+
const Queries1 = {
225+
get: () =>
226+
queryOptions({
227+
queryKey: ['key1'],
228+
queryFn: () => Promise.resolve(1),
229+
}),
230+
}
231+
const Queries2 = {
232+
get: () =>
233+
queryOptions({
234+
queryKey: ['key2'],
235+
queryFn: () => Promise.resolve(true),
236+
}),
237+
}
238+
239+
const queries1List = [1, 2, 3].map(() => ({ ...Queries1.get() }))
240+
const { value: queriesState } = useQueries({
241+
queries: [...queries1List, { ...Queries2.get() }],
242+
})
243+
244+
expectTypeOf(queriesState).toEqualTypeOf<
245+
[
246+
...Array<QueryObserverResult<number, Error>>,
247+
QueryObserverResult<boolean, Error>,
248+
]
249+
>()
250+
251+
expectTypeOf(queriesState[0].data).toEqualTypeOf<
252+
number | boolean | undefined
253+
>()
254+
})
222255
})

0 commit comments

Comments
 (0)