diff --git a/.changeset/silver-coins-mix.md b/.changeset/silver-coins-mix.md new file mode 100644 index 00000000000..efc41ecea9f --- /dev/null +++ b/.changeset/silver-coins-mix.md @@ -0,0 +1,11 @@ +--- +'@tanstack/angular-query-experimental': minor +'@tanstack/svelte-query': minor +'@tanstack/react-query': minor +'@tanstack/preact-query': minor +'@tanstack/solid-query': minor +'@tanstack/query-core': minor +'@tanstack/vue-query': minor +--- + +feat(query-core): Allow disabling structuralSharing for useQueries. diff --git a/docs/framework/react/reference/useQueries.md b/docs/framework/react/reference/useQueries.md index d41a73a2b1c..f330291e21f 100644 --- a/docs/framework/react/reference/useQueries.md +++ b/docs/framework/react/reference/useQueries.md @@ -24,6 +24,12 @@ The `useQueries` hook accepts an options object with a **queries** key whose val - Use this to provide a custom QueryClient. Otherwise, the one from the nearest context will be used. - `combine?: (result: UseQueriesResults) => TCombinedResult` - Use this to combine the results of the queries into a single value. +- `structuralSharing?: boolean | ((oldData: unknown | undefined, newData: unknown) => unknown)` + - Optional + - Defaults to `true`. + - Only applies when `combine` is provided. + - If set to `false`, structural sharing between query results will be disabled. + - If set to a function, the old and new data values will be passed through this function, which should combine them into resolved data for the query. This way, you can retain references from the old data to improve performance even when that data contains non-serializable values. > Having the same query key more than once in the array of query objects may cause some data to be shared between queries. To avoid this, consider de-duplicating the queries and map the results back to the desired structure. @@ -37,7 +43,7 @@ The `useQueries` hook returns an array with all the query results. The order ret ## Combine -If you want to combine `data` (or other Query information) from the results into a single value, you can use the `combine` option. The result will be structurally shared to be as referentially stable as possible. +If you want to combine `data` (or other Query information) from the results into a single value, you can use the `combine` option. The result will be structurally shared to be as referentially stable as possible. If you want to disable structural sharing for the combined result, you can set the `structuralSharing` option to `false`, or provide a custom function to implement your own structural sharing logic. ```tsx const ids = [1, 2, 3] diff --git a/packages/angular-query-experimental/src/inject-queries.ts b/packages/angular-query-experimental/src/inject-queries.ts index 2f201799e74..c66e5b72d53 100644 --- a/packages/angular-query-experimental/src/inject-queries.ts +++ b/packages/angular-query-experimental/src/inject-queries.ts @@ -211,6 +211,15 @@ export interface InjectQueriesOptions< ...{ [K in keyof T]: GetCreateQueryOptionsForCreateQueries }, ] combine?: (result: QueriesResults) => TCombinedResult + /** + * Set this to `false` to disable structural sharing between query results. + * Set this to a function which accepts the old and new data and returns resolved data of the same type to implement custom structural sharing logic. + * Only applies when `combine` is provided. + * Defaults to `true`. + */ + structuralSharing?: + | boolean + | ((oldData: unknown | undefined, newData: unknown) => unknown) } /** @@ -270,7 +279,7 @@ export function injectQueries< const optimisticResultSignal = computed(() => observerSignal().getOptimisticResult( defaultedQueries(), - (optionsSignal() as QueriesObserverOptions).combine, + optionsSignal() as QueriesObserverOptions, ), ) diff --git a/packages/preact-query/src/useQueries.ts b/packages/preact-query/src/useQueries.ts index be57d2aebd6..fe30ec0d361 100644 --- a/packages/preact-query/src/useQueries.ts +++ b/packages/preact-query/src/useQueries.ts @@ -216,6 +216,15 @@ export function useQueries< | readonly [...QueriesOptions] | readonly [...{ [K in keyof T]: GetUseQueryOptionsForUseQueries }] combine?: (result: QueriesResults) => TCombinedResult + /** + * Set this to `false` to disable structural sharing between query results. + * Set this to a function which accepts the old and new data and returns resolved data of the same type to implement custom structural sharing logic. + * Only applies when `combine` is provided. + * Defaults to `true`. + */ + structuralSharing?: + | boolean + | ((oldData: unknown | undefined, newData: unknown) => unknown) subscribed?: boolean }, queryClient?: QueryClient, @@ -261,7 +270,7 @@ export function useQueries< const [optimisticResult, getCombinedResult, trackResult] = observer.getOptimisticResult( defaultedQueries, - (options as QueriesObserverOptions).combine, + options as QueriesObserverOptions, ) const shouldSubscribe = !isRestoring && options.subscribed !== false diff --git a/packages/query-core/src/__tests__/queriesObserver.test.tsx b/packages/query-core/src/__tests__/queriesObserver.test.tsx index 2f7080ec60c..e448d4f4009 100644 --- a/packages/query-core/src/__tests__/queriesObserver.test.tsx +++ b/packages/query-core/src/__tests__/queriesObserver.test.tsx @@ -300,6 +300,7 @@ describe('queriesObserver', () => { { queryKey: key1, queryFn: queryFn1 }, ], undefined, + undefined, )[0], ) @@ -414,6 +415,7 @@ describe('queriesObserver', () => { const [initialRaw, getInitialCombined] = observer.getOptimisticResult( [{ queryKey: key1, queryFn: queryFn1 }], combine, + undefined, ) const initialCombined = getInitialCombined(initialRaw) @@ -426,6 +428,7 @@ describe('queriesObserver', () => { const [newRaw, getNewCombined] = observer.getOptimisticResult( newQueries, combine, + undefined, ) const newCombined = getNewCombined(newRaw) @@ -461,6 +464,7 @@ describe('queriesObserver', () => { { queryKey: key2, queryFn: queryFn2 }, ], combine, + undefined, ) const initialCombined = getInitialCombined(initialRaw) @@ -470,6 +474,7 @@ describe('queriesObserver', () => { const [newRaw, getNewCombined] = observer.getOptimisticResult( newQueries, combine, + undefined, ) const newCombined = getNewCombined(newRaw) @@ -497,6 +502,7 @@ describe('queriesObserver', () => { const [initialRaw, getInitialCombined] = observer.getOptimisticResult( [{ queryKey: key1, queryFn: queryFn1 }], combine, + undefined, ) const initialCombined = getInitialCombined(initialRaw) @@ -505,6 +511,7 @@ describe('queriesObserver', () => { const [newRaw, getNewCombined] = observer.getOptimisticResult( [{ queryKey: key2, queryFn: queryFn2 }], combine, + undefined, ) const newCombined = getNewCombined(newRaw) @@ -530,6 +537,7 @@ describe('queriesObserver', () => { { queryKey: key2, queryFn: queryFn2 }, ], undefined, + false, ) const trackedResults = trackResult() @@ -546,4 +554,266 @@ describe('queriesObserver', () => { trackPropSpy.mockRestore() }) + + test('should not use structural sharing when structuralSharing is false', () => { + const key1 = queryKey() + const queryFn1 = vi.fn().mockReturnValue(1) + + queryClient.setQueryData(key1, 'cached-1') + + // Create a combine function that returns a new object with a nested array + const nestedArray = ['a', 'b', 'c'] + const combine = vi.fn((_results: Array) => ({ + nested: nestedArray, + })) + + const observer = new QueriesObserver<{ + nested: Array + }>(queryClient, [{ queryKey: key1, queryFn: queryFn1 }], { + combine, + structuralSharing: false, + }) + + const [initialRaw, getInitialCombined] = observer.getOptimisticResult( + [{ queryKey: key1, queryFn: queryFn1 }], + combine, + false, + ) + const initialCombined = getInitialCombined(initialRaw) + + // Create a new combine function reference to trigger re-combine + // but with the same nested array content + const combine2 = vi.fn((_results: Array) => ({ + nested: ['a', 'b', 'c'], // Same content, different reference + })) + + const [newRaw, getNewCombined] = observer.getOptimisticResult( + [{ queryKey: key1, queryFn: queryFn1 }], + combine2, + false, + ) + const newCombined = getNewCombined(newRaw) + + // With structuralSharing: false, even though the nested array has the same content, + // the reference should NOT be preserved (no replaceEqualDeep optimization) + expect(newCombined.nested).toEqual(initialCombined.nested) + expect(newCombined.nested).not.toBe(initialCombined.nested) + }) + + test('should use structural sharing when structuralSharing is true', () => { + const key1 = queryKey() + const queryFn1 = vi.fn().mockReturnValue(1) + + queryClient.setQueryData(key1, 'cached-1') + + // Create a combine function that returns a new object with a nested array + const combine = vi.fn((_results: Array) => ({ + nested: ['a', 'b', 'c'], + })) + + const observer = new QueriesObserver<{ + nested: Array + }>(queryClient, [{ queryKey: key1, queryFn: queryFn1 }], { + combine, + structuralSharing: true, + }) + + const [initialRaw, getInitialCombined] = observer.getOptimisticResult( + [{ queryKey: key1, queryFn: queryFn1 }], + combine, + true, + ) + const initialCombined = getInitialCombined(initialRaw) + + // Create a new combine function reference to trigger re-combine + // but with the same nested array content + const combine2 = vi.fn((_results: Array) => ({ + nested: ['a', 'b', 'c'], // Same content, different reference + })) + + const [newRaw, getNewCombined] = observer.getOptimisticResult( + [{ queryKey: key1, queryFn: queryFn1 }], + combine2, + true, + ) + const newCombined = getNewCombined(newRaw) + + // With structuralSharing: true, replaceEqualDeep should preserve the reference + // since the nested array has the same content + expect(newCombined.nested).toEqual(initialCombined.nested) + expect(newCombined.nested).toBe(initialCombined.nested) + }) + + test('should use custom structuralSharing function when provided', () => { + const combine = vi.fn((results: Array) => ({ + count: results.length, + data: results.map((r) => r.data), + })) + + const customStructuralSharing = vi.fn( + (_oldData: unknown, newData: unknown) => { + // Custom logic: always return the new data but with a marker + return { ...(newData as object), customShared: true } + }, + ) + + const key1 = queryKey() + const queryFn1 = vi.fn().mockReturnValue(1) + + queryClient.setQueryData(key1, 'cached-1') + + const observer = new QueriesObserver<{ + count: number + data: Array + customShared?: boolean + }>(queryClient, [{ queryKey: key1, queryFn: queryFn1 }], { + combine, + structuralSharing: customStructuralSharing, + }) + + const [initialRaw, getInitialCombined] = observer.getOptimisticResult( + [{ queryKey: key1, queryFn: queryFn1 }], + combine, + customStructuralSharing, + ) + const initialCombined = getInitialCombined(initialRaw) + + expect(initialCombined.count).toBe(1) + expect(initialCombined.customShared).toBe(true) + expect(customStructuralSharing).toHaveBeenCalledTimes(1) + }) + + test('should pass old and new data to custom structuralSharing function', () => { + const combine = vi.fn((results: Array) => ({ + count: results.length, + data: results.map((r) => r.data), + })) + + const customStructuralSharing = vi.fn( + (_oldData: unknown, newData: unknown) => { + // Return new data with reference to old data for testing + return newData + }, + ) + + const key1 = queryKey() + const key2 = queryKey() + const queryFn1 = vi.fn().mockReturnValue(1) + const queryFn2 = vi.fn().mockReturnValue(2) + + queryClient.setQueryData(key1, 'cached-1') + + const observer = new QueriesObserver<{ + count: number + data: Array + }>(queryClient, [{ queryKey: key1, queryFn: queryFn1 }], { + combine, + structuralSharing: customStructuralSharing, + }) + + // First call + const [initialRaw, getInitialCombined] = observer.getOptimisticResult( + [{ queryKey: key1, queryFn: queryFn1 }], + combine, + customStructuralSharing, + ) + const initialCombined = getInitialCombined(initialRaw) + + expect(initialCombined.count).toBe(1) + + // Second call with different queries - should trigger combine again + const [newRaw, getNewCombined] = observer.getOptimisticResult( + [ + { queryKey: key1, queryFn: queryFn1 }, + { queryKey: key2, queryFn: queryFn2 }, + ], + combine, + customStructuralSharing, + ) + const newCombined = getNewCombined(newRaw) + + expect(newCombined.count).toBe(2) + // Custom structural sharing function should have been called twice + expect(customStructuralSharing).toHaveBeenCalledTimes(2) + // First call should have undefined as oldData (no previous combined result) + expect(customStructuralSharing).toHaveBeenNthCalledWith( + 1, + undefined, + expect.objectContaining({ count: 1 }), + ) + // Second call should have the previous combined result as oldData + expect(customStructuralSharing).toHaveBeenNthCalledWith( + 2, + expect.objectContaining({ count: 1 }), + expect.objectContaining({ count: 2 }), + ) + }) + + test('should retain references with custom structuralSharing function', () => { + // This test verifies that a custom structuralSharing function can retain references + const existingArray = [1, 2, 3] + + const combine = vi.fn((results: Array) => ({ + count: results.length, + data: results.map((r) => r.data), + existingArray, + })) + + const customStructuralSharing = vi.fn( + (oldData: unknown, newData: unknown) => { + const oldTyped = oldData as + | { existingArray?: Array } + | undefined + const newTyped = newData as { existingArray: Array } + // Retain the existingArray reference from old data if it deeply equals + if ( + oldTyped?.existingArray && + JSON.stringify(oldTyped.existingArray) === + JSON.stringify(newTyped.existingArray) + ) { + return { ...newTyped, existingArray: oldTyped.existingArray } + } + return newTyped + }, + ) + + const key1 = queryKey() + const queryFn1 = vi.fn().mockReturnValue(1) + + queryClient.setQueryData(key1, 'cached-1') + + const observer = new QueriesObserver<{ + count: number + data: Array + existingArray: Array + }>(queryClient, [{ queryKey: key1, queryFn: queryFn1 }], { + combine, + structuralSharing: customStructuralSharing, + }) + + const [initialRaw, getInitialCombined] = observer.getOptimisticResult( + [{ queryKey: key1, queryFn: queryFn1 }], + combine, + customStructuralSharing, + ) + const initialCombined = getInitialCombined(initialRaw) + const initialArrayRef = initialCombined.existingArray + + // Trigger a re-combine by changing the combine function reference + const combine2 = vi.fn((results: Array) => ({ + count: results.length, + data: results.map((r) => r.data), + existingArray, // Same array content + })) + + const [newRaw, getNewCombined] = observer.getOptimisticResult( + [{ queryKey: key1, queryFn: queryFn1 }], + combine2, + customStructuralSharing, + ) + const newCombined = getNewCombined(newRaw) + + // The existingArray reference should be retained from the old data + expect(newCombined.existingArray).toBe(initialArrayRef) + }) }) diff --git a/packages/query-core/src/queriesObserver.ts b/packages/query-core/src/queriesObserver.ts index 67dd088f9ae..1f094f81c07 100644 --- a/packages/query-core/src/queriesObserver.ts +++ b/packages/query-core/src/queriesObserver.ts @@ -1,7 +1,7 @@ import { notifyManager } from './notifyManager' import { QueryObserver } from './queryObserver' import { Subscribable } from './subscribable' -import { replaceEqualDeep, shallowEqualObjects } from './utils' +import { replaceData, shallowEqualObjects } from './utils' import type { DefaultedQueryObserverOptions, QueryObserverOptions, @@ -30,6 +30,15 @@ export interface QueriesObserverOptions< TCombinedResult = Array, > { combine?: CombineFn + /** + * Set this to `false` to disable structural sharing between query results. + * Set this to a function which accepts the old and new data and returns resolved data of the same type to implement custom structural sharing logic. + * Only applies when `combine` is provided. + * Defaults to `true`. + */ + structuralSharing?: + | boolean + | ((oldData: unknown | undefined, newData: unknown) => unknown) } export class QueriesObserver< @@ -171,7 +180,7 @@ export class QueriesObserver< getOptimisticResult( queries: Array, - combine: CombineFn | undefined, + options?: QueriesObserverOptions, ): [ rawResult: Array, combineResult: (r?: Array) => TCombinedResult, @@ -188,7 +197,7 @@ export class QueriesObserver< return [ result, (r?: Array) => { - return this.#combineResult(r ?? result, combine, queryHashes) + return this.#combineResult(r ?? result, options, queryHashes) }, () => { return this.#trackResult(result, matches) @@ -215,9 +224,11 @@ export class QueriesObserver< #combineResult( input: Array, - combine: CombineFn | undefined, + options?: QueriesObserverOptions, queryHashes?: Array, ): TCombinedResult { + const combine = options?.combine + const structuralSharing = options?.structuralSharing ?? true if (combine) { const lastHashes = this.#lastQueryHashes const queryHashesChanged = @@ -238,9 +249,14 @@ export class QueriesObserver< if (queryHashes !== undefined) { this.#lastQueryHashes = queryHashes } - this.#combinedResult = replaceEqualDeep( + + this.#combinedResult = replaceData( this.#combinedResult, combine(input), + { + structuralSharing, + queryHash: queryHashes, + }, ) } @@ -296,7 +312,7 @@ export class QueriesObserver< if (this.hasListeners()) { const previousResult = this.#combinedResult const newTracked = this.#trackResult(this.#result, this.#observerMatches) - const newResult = this.#combineResult(newTracked, this.#options?.combine) + const newResult = this.#combineResult(newTracked, this.#options) if (previousResult !== newResult) { notifyManager.batch(() => { diff --git a/packages/query-core/src/utils.ts b/packages/query-core/src/utils.ts index 27d44bc98b3..ff8e2da15eb 100644 --- a/packages/query-core/src/utils.ts +++ b/packages/query-core/src/utils.ts @@ -381,7 +381,10 @@ export function sleep(timeout: number): Promise { export function replaceData< TData, - TOptions extends QueryOptions, + TOptions extends { + structuralSharing?: boolean | ((prev: unknown, data: unknown) => unknown) + queryHash?: string | Array + }, >(prevData: TData | undefined, data: TData, options: TOptions): TData { if (typeof options.structuralSharing === 'function') { return options.structuralSharing(prevData, data) as TData @@ -390,8 +393,14 @@ export function replaceData< try { return replaceEqualDeep(prevData, data) } catch (error) { + let hashInfo = '' + if (Array.isArray(options.queryHash)) { + hashInfo = `queryHashes: [${options.queryHash.join(', ')}]` + } else if (options.queryHash) { + hashInfo = `queryHash: ${options.queryHash}` + } console.error( - `Structural sharing requires data to be JSON serializable. To fix this, turn off structuralSharing or return JSON-serializable data from your queryFn. [${options.queryHash}]: ${error}`, + `Structural sharing requires data to be JSON serializable. To fix this, turn off structuralSharing or return JSON-serializable data from your queryFn. [${hashInfo}]: ${error}`, ) // Prevent the replaceEqualDeep from being called again down below. diff --git a/packages/react-query/src/useQueries.ts b/packages/react-query/src/useQueries.ts index de179837a5f..817ec36b8a5 100644 --- a/packages/react-query/src/useQueries.ts +++ b/packages/react-query/src/useQueries.ts @@ -216,6 +216,15 @@ export function useQueries< | readonly [...QueriesOptions] | readonly [...{ [K in keyof T]: GetUseQueryOptionsForUseQueries }] combine?: (result: QueriesResults) => TCombinedResult + /** + * Set this to `false` to disable structural sharing between query results. + * Set this to a function which accepts the old and new data and returns resolved data of the same type to implement custom structural sharing logic. + * Only applies when `combine` is provided. + * Defaults to `true`. + */ + structuralSharing?: + | boolean + | ((oldData: unknown | undefined, newData: unknown) => unknown) subscribed?: boolean }, queryClient?: QueryClient, @@ -262,7 +271,7 @@ export function useQueries< const [optimisticResult, getCombinedResult, trackResult] = observer.getOptimisticResult( defaultedQueries, - (options as QueriesObserverOptions).combine, + options as QueriesObserverOptions, ) const shouldSubscribe = !isRestoring && options.subscribed !== false diff --git a/packages/solid-query/src/useQueries.ts b/packages/solid-query/src/useQueries.ts index 1e5592775db..2182d9c7c35 100644 --- a/packages/solid-query/src/useQueries.ts +++ b/packages/solid-query/src/useQueries.ts @@ -193,6 +193,15 @@ export function useQueries< | readonly [...QueriesOptions] | readonly [...{ [K in keyof T]: GetOptions }] combine?: (result: QueriesResults) => TCombinedResult + /** + * Set this to `false` to disable structural sharing between query results. + * Set this to a function which accepts the old and new data and returns resolved data of the same type to implement custom structural sharing logic. + * Only applies when `combine` is provided. + * Defaults to `true`. + */ + structuralSharing?: + | boolean + | ((oldData: unknown | undefined, newData: unknown) => unknown) }>, queryClient?: Accessor, ): TCombinedResult { @@ -218,6 +227,7 @@ export function useQueries< queriesOptions().combine ? ({ combine: queriesOptions().combine, + structuralSharing: queriesOptions().structuralSharing, } as QueriesObserverOptions) : undefined, ) @@ -225,7 +235,7 @@ export function useQueries< const [state, setState] = createStore( observer.getOptimisticResult( defaultedQueries(), - (queriesOptions() as QueriesObserverOptions).combine, + queriesOptions() as QueriesObserverOptions, )[1](), ) @@ -236,8 +246,7 @@ export function useQueries< setState( observer.getOptimisticResult( defaultedQueries(), - (queriesOptions() as QueriesObserverOptions) - .combine, + queriesOptions() as QueriesObserverOptions, )[1](), ), ), @@ -303,22 +312,14 @@ export function useQueries< onMount(() => { observer.setQueries( defaultedQueries(), - queriesOptions().combine - ? ({ - combine: queriesOptions().combine, - } as QueriesObserverOptions) - : undefined, + queriesOptions() as QueriesObserverOptions, ) }) createComputed(() => { observer.setQueries( defaultedQueries(), - queriesOptions().combine - ? ({ - combine: queriesOptions().combine, - } as QueriesObserverOptions) - : undefined, + queriesOptions() as QueriesObserverOptions, ) }) diff --git a/packages/svelte-query/src/createQueries.svelte.ts b/packages/svelte-query/src/createQueries.svelte.ts index dec57561299..a73d46d7c1e 100644 --- a/packages/svelte-query/src/createQueries.svelte.ts +++ b/packages/svelte-query/src/createQueries.svelte.ts @@ -197,13 +197,23 @@ export function createQueries< ...{ [K in keyof T]: GetCreateQueryOptionsForCreateQueries }, ] combine?: (result: QueriesResults) => TCombinedResult + /** + * Set this to `false` to disable structural sharing between query results. + * Set this to a function which accepts the old and new data and returns resolved data of the same type to implement custom structural sharing logic. + * Only applies when `combine` is provided. + * Defaults to `true`. + */ + structuralSharing?: + | boolean + | ((oldData: unknown | undefined, newData: unknown) => unknown) }>, queryClient?: Accessor, ): TCombinedResult { const client = $derived(useQueryClient(queryClient?.())) const isRestoring = useIsRestoring() - const { queries, combine } = $derived.by(createQueriesOptions) + const { queries, ...derivedCreateQueriesOptions } = + $derived.by(createQueriesOptions) const resolvedQueryOptions = $derived( queries.map((opts) => { const resolvedOptions = client.defaultQueryOptions(opts) @@ -220,14 +230,14 @@ export function createQueries< new QueriesObserver( client, resolvedQueryOptions, - combine as QueriesObserverOptions, + derivedCreateQueriesOptions as QueriesObserverOptions, ), ) function createResult() { const [_, getCombinedResult, trackResult] = observer.getOptimisticResult( resolvedQueryOptions, - combine as QueriesObserverOptions['combine'], + derivedCreateQueriesOptions as QueriesObserverOptions, ) return getCombinedResult(trackResult()) } @@ -244,9 +254,10 @@ export function createQueries< }) $effect.pre(() => { - observer.setQueries(resolvedQueryOptions, { - combine, - } as QueriesObserverOptions) + observer.setQueries( + resolvedQueryOptions, + derivedCreateQueriesOptions as QueriesObserverOptions, + ) update(createResult()) }) diff --git a/packages/vue-query/src/useQueries.ts b/packages/vue-query/src/useQueries.ts index f290976a145..f59fb188e30 100644 --- a/packages/vue-query/src/useQueries.ts +++ b/packages/vue-query/src/useQueries.ts @@ -248,6 +248,15 @@ export function useQueries< ] > combine?: (result: UseQueriesResults) => TCombinedResult + /** + * Set this to `false` to disable structural sharing between query results. + * Set this to a function which accepts the old and new data and returns resolved data of the same type to implement custom structural sharing logic. + * Only applies when `combine` is provided. + * Defaults to `true`. + */ + structuralSharing?: + | boolean + | ((oldData: unknown | undefined, newData: unknown) => unknown) }, queryClient?: QueryClient, ): Readonly> { @@ -295,7 +304,7 @@ export function useQueries< const getOptimisticResult = () => { const [results, getCombinedResult] = observer.getOptimisticResult( defaultedQueries.value, - (options as QueriesObserverOptions).combine, + options as QueriesObserverOptions, ) return getCombinedResult( @@ -305,7 +314,7 @@ export function useQueries< refetch: async (...args: Array) => { const [{ [index]: query }] = observer.getOptimisticResult( defaultedQueries.value, - (options as QueriesObserverOptions).combine, + options as QueriesObserverOptions, ) return query!.refetch(...args)