Type Error when using `useQueries` with results returning different types, generated from an array
Describe the bug
I want to use useQueries to fetch x queries every time, and another y queries, whose count is not known at compile time.
Here is a short example (playground below):
export const useThings = () => {
const foos = [1, 2, 3].map(() => ({ ...FooQueries.get() }));
return useQueries({
queries: [...foos, { ...BarQueries.get(), }],
});
};
There is a type error that says:
The type 'QueryKey' is 'readonly' and cannot be assigned to the mutable type 'string[]'
Your minimal, reproducible example
https://tsplay.dev/mq2d2W
Steps to reproduce
use useQueries with a variable count of queries that have different return type than other queries
Expected behavior
Expected to be able to pass an array of queries with variable length to useQueries, along with other queries with different return type.
How often does this bug happen?
Every time
Screenshots or Videos
No response
Platform
- MacOS
Tanstack Query adapter
None
TanStack Query version
5.52.1
TypeScript version
5.5.4
Additional context
Edit: I tinkered a bit more with this and it appears that the issue is with the queryOptions function. Here is the full error:
Type '{ queryKey: string[] & { [dataTagSymbol]: number; }; enabled?: Enabled<number, Error, number, string[]> | undefined; staleTime?: StaleTime<number, Error, number, string[]> | undefined; ... 25 more ...; maxPages?: number | undefined; }' is not assignable to type 'UseQueryOptionsForUseQueries<unknown, Error, unknown, QueryKey>'.
Type '{ queryKey: string[] & { [dataTagSymbol]: number; }; enabled?: Enabled<number, Error, number, string[]> | undefined; staleTime?: StaleTime<number, Error, number, string[]> | undefined; ... 25 more ...; maxPages?: number | undefined; }' is not assignable to type 'OmitKeyof<UseQueryOptions<unknown, Error, unknown, QueryKey>, "placeholderData">'.
Types of property 'enabled' are incompatible.
Type 'Enabled<number, Error, number, string[]> | undefined' is not assignable to type 'Enabled<unknown, Error, unknown, QueryKey> | undefined'.
Type '(query: Query<number, Error, number, string[]>) => boolean' is not assignable to type 'Enabled<unknown, Error, unknown, QueryKey> | undefined'.
Type '(query: Query<number, Error, number, string[]>) => boolean' is not assignable to type '(query: Query<unknown, Error, unknown, QueryKey>) => boolean'.
Types of parameters 'query' and 'query' are incompatible.
Type 'Query<unknown, Error, unknown, QueryKey>' is not assignable to type 'Query<number, Error, number, string[]>'.
Types of property 'queryKey' are incompatible.
The type 'QueryKey' is 'readonly' and cannot be assigned to the mutable type 'string[]'.ts(2322)
Edit 2: If all queries return the same type of result, it works.
different return types generally work, I think this is rather an issue with queryOptions. If we remove queryOptions, it also works. playground.
Or rather, a combination of the two:
-
queryOptionsforBarQueriesreturns the typeUseQueryOptions<boolean, Error, boolean, string[]>, so theTQueryKeytype parameter is inferred to bestring[] -
useQueriesinfers the type to beUseQueryOptionsForUseQueries<unknown, Error, unknown, QueryKey>. Here, theTQueryKeytype parameter is inferred toQueryKey, which is our upper bound, and that isreadonly unknown[]
So it seems the readonly here makes all the difference.
But even if we get rid of that by making our keys of type QueryKey, we get a different error:
Type 'QueryFunction<unknown, QueryKey, never>' is not assignable to type 'QueryFunction<number, QueryKey, never>'.
Type 'unknown' is not assignable to type 'number'.(2322)
This is again because inference of UseQueryOptionsForUseQueries, where data falls back to unknown.
Thinking about it some more - I don't think variadic queries of different types is something that we can infer / support. For variadic queries, they have to be the same type. Static queries (tuples) of different types are supported.
In your example, ideally the last element in the result array would be typed as bar, and all the others as foo. But I think this is way to complex to achieve, which is why we fallback to unknown, and that then doesn't really work with queryOptions.
I think you have to do manual type annotations for this one. Like, what should the data type of useThings be? number | boolean - a union of what all queries can return, or just unknown?
I agree that typing this will be way too complex.
To answer your question, I think that the type can be number | boolean[], if the call looks like this (the spread is at the end):
export const useThings = () => {
const foos = [1, 2, 3].map(() => ({ ...FooQueries.get() }));
return useQueries({
queries: [{ ...BarQueries.get() }, ...foos],
});
};
if you want to make that work, please take a stab at it.