Skip to content

Commit 0f01fbd

Browse files
fix(solid-query): allow combine to return arbitrary object shape (#7522)
Relax `TCombinedResult` from `extends QueriesResults<T>` to `extends object` on `useQueries` / `createQueries`, mirroring the React adapter's flexibility. A `combine` callback can now return any object literal — e.g. `combine: (results) => ({ data: results.every(r => r.data) })` — instead of being forced to match the array of per-query results. The proxy/resource logic that follows still needs an array view of the store, so `state` is aliased to `Array<QueryObserverResult>` for those internal operations only. The reactive Solid store is preserved at runtime; the cast only affects TypeScript. Co-Authored-By: Claude <noreply@anthropic.com>
1 parent 93b2845 commit 0f01fbd

3 files changed

Lines changed: 52 additions & 6 deletions

File tree

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
'@tanstack/solid-query': patch
3+
---
4+
5+
fix(solid-query): allow `combine` to return a custom result shape from `useQueries` / `createQueries`, matching the behavior of the React adapter. Relaxed the `TCombinedResult` generic from `extends QueriesResults<T>` to `extends object`, so a `combine` callback can return any object literal (e.g. `{ data: boolean }`) instead of being constrained to the array of per-query results (#7522).

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

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -289,4 +289,39 @@ describe('useQueries', () => {
289289
},
290290
}))
291291
})
292+
293+
it('combine should allow returning an arbitrary object shape (#7522)', () => {
294+
const result = useQueries(() => ({
295+
queries: [
296+
{
297+
queryKey: queryKey(),
298+
queryFn: () => Promise.resolve(true),
299+
},
300+
{
301+
queryKey: queryKey(),
302+
queryFn: () => Promise.resolve(false),
303+
},
304+
],
305+
combine: (results) => ({
306+
data: results.every((r) => r.data),
307+
}),
308+
}))
309+
310+
expectTypeOf(result).toEqualTypeOf<{ data: boolean }>()
311+
})
312+
313+
it('combine should infer custom object shape from .map() queries (#7522)', () => {
314+
const list = ['test1', 'test2']
315+
const result = useQueries(() => ({
316+
queries: list.map((key) => ({
317+
queryKey: ['key', key],
318+
queryFn: () => true,
319+
})),
320+
combine: (results) => ({
321+
data: results.every((r) => r.data),
322+
}),
323+
}))
324+
325+
expectTypeOf(result).toEqualTypeOf<{ data: boolean }>()
326+
})
292327
})

packages/solid-query/src/useQueries.ts

Lines changed: 12 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -186,7 +186,7 @@ type QueriesResults<
186186

187187
export function useQueries<
188188
T extends Array<any>,
189-
TCombinedResult extends QueriesResults<T> = QueriesResults<T>,
189+
TCombinedResult extends object = QueriesResults<T>,
190190
>(
191191
queriesOptions: Accessor<{
192192
queries:
@@ -243,11 +243,17 @@ export function useQueries<
243243
),
244244
)
245245

246+
// Internal "view" of the underlying per-query results. `state` is typed as
247+
// `TCombinedResult` (which may be a user-defined non-array shape via `combine`),
248+
// but the proxy/resource logic below treats it as an array of `QueryObserverResult`.
249+
// The cast preserves Solid's reactive store at runtime — it only affects TypeScript.
250+
const queryResults = state as unknown as Array<QueryObserverResult>
251+
246252
const dataResources = createMemo(
247253
on(
248-
() => state.length,
254+
() => queryResults.length,
249255
() =>
250-
state.map((queryRes) => {
256+
queryResults.map((queryRes) => {
251257
const dataPromise = () =>
252258
new Promise((resolve) => {
253259
if (queryRes.isFetching && queryRes.isLoading) return
@@ -262,7 +268,7 @@ export function useQueries<
262268
const dataResources_ = dataResources()
263269
for (let index = 0; index < dataResources_.length; index++) {
264270
const dataResource = dataResources_[index]!
265-
dataResource[1].mutate(() => unwrap(state[index]!.data))
271+
dataResource[1].mutate(() => unwrap(queryResults[index]!.data))
266272
dataResource[1].refetch()
267273
}
268274
})
@@ -278,7 +284,7 @@ export function useQueries<
278284
const unwrappedResult = { ...unwrap(result[index]) }
279285
// @ts-expect-error typescript pedantry regarding the possible range of index
280286
setState(index, unwrap(unwrappedResult))
281-
dataResource[1].mutate(() => unwrap(state[index]!.data))
287+
dataResource[1].mutate(() => unwrap(queryResults[index]!.data))
282288
dataResource[1].refetch()
283289
}
284290
})
@@ -332,7 +338,7 @@ export function useQueries<
332338
})
333339

334340
const getProxies = () =>
335-
state.map((s, index) => {
341+
queryResults.map((s, index) => {
336342
return new Proxy(s, handler(index))
337343
})
338344

0 commit comments

Comments
 (0)