diff --git a/.changeset/fix-solid-query-combine-custom-result-shape.md b/.changeset/fix-solid-query-combine-custom-result-shape.md new file mode 100644 index 00000000000..f1670c0b389 --- /dev/null +++ b/.changeset/fix-solid-query-combine-custom-result-shape.md @@ -0,0 +1,5 @@ +--- +'@tanstack/solid-query': patch +--- + +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` 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). diff --git a/packages/solid-query/src/__tests__/useQueries.test-d.tsx b/packages/solid-query/src/__tests__/useQueries.test-d.tsx index 3567f77bfc6..938483fa4ef 100644 --- a/packages/solid-query/src/__tests__/useQueries.test-d.tsx +++ b/packages/solid-query/src/__tests__/useQueries.test-d.tsx @@ -289,4 +289,39 @@ describe('useQueries', () => { }, })) }) + + it('combine should allow returning an arbitrary object shape (#7522)', () => { + const result = useQueries(() => ({ + queries: [ + { + queryKey: queryKey(), + queryFn: () => Promise.resolve(true), + }, + { + queryKey: queryKey(), + queryFn: () => Promise.resolve(false), + }, + ], + combine: (results) => ({ + data: results.every((r) => r.data), + }), + })) + + expectTypeOf(result).toEqualTypeOf<{ data: boolean }>() + }) + + it('combine should infer custom object shape from .map() queries (#7522)', () => { + const list = ['test1', 'test2'] + const result = useQueries(() => ({ + queries: list.map((key) => ({ + queryKey: ['key', key], + queryFn: () => true, + })), + combine: (results) => ({ + data: results.every((r) => r.data), + }), + })) + + expectTypeOf(result).toEqualTypeOf<{ data: boolean }>() + }) }) diff --git a/packages/solid-query/src/useQueries.ts b/packages/solid-query/src/useQueries.ts index 1e5592775db..0cb6861306b 100644 --- a/packages/solid-query/src/useQueries.ts +++ b/packages/solid-query/src/useQueries.ts @@ -186,7 +186,7 @@ type QueriesResults< export function useQueries< T extends Array, - TCombinedResult extends QueriesResults = QueriesResults, + TCombinedResult extends object = QueriesResults, >( queriesOptions: Accessor<{ queries: @@ -243,11 +243,17 @@ export function useQueries< ), ) + // Internal "view" of the underlying per-query results. `state` is typed as + // `TCombinedResult` (which may be a user-defined non-array shape via `combine`), + // but the proxy/resource logic below treats it as an array of `QueryObserverResult`. + // The cast preserves Solid's reactive store at runtime — it only affects TypeScript. + const queryResults = state as unknown as Array + const dataResources = createMemo( on( - () => state.length, + () => queryResults.length, () => - state.map((queryRes) => { + queryResults.map((queryRes) => { const dataPromise = () => new Promise((resolve) => { if (queryRes.isFetching && queryRes.isLoading) return @@ -262,7 +268,7 @@ export function useQueries< const dataResources_ = dataResources() for (let index = 0; index < dataResources_.length; index++) { const dataResource = dataResources_[index]! - dataResource[1].mutate(() => unwrap(state[index]!.data)) + dataResource[1].mutate(() => unwrap(queryResults[index]!.data)) dataResource[1].refetch() } }) @@ -278,7 +284,7 @@ export function useQueries< const unwrappedResult = { ...unwrap(result[index]) } // @ts-expect-error typescript pedantry regarding the possible range of index setState(index, unwrap(unwrappedResult)) - dataResource[1].mutate(() => unwrap(state[index]!.data)) + dataResource[1].mutate(() => unwrap(queryResults[index]!.data)) dataResource[1].refetch() } }) @@ -332,7 +338,7 @@ export function useQueries< }) const getProxies = () => - state.map((s, index) => { + queryResults.map((s, index) => { return new Proxy(s, handler(index)) })