Open
Description
Describe the bug
Related to #4770, revitalizing since NoInfer
/overloads can at least partially help.
TQueryFnData
is directly inferred fromqueryFn
specified in the hook optionsoptions
TData
defaults toTQueryFnData
, but can be specified via a generic or annotated return type- When
TData
generic is populated (by inference, or explicitly) , nothing enforces that there's type compatibility until you put aselect
function, leading to uncaught runtime breaks.
Your minimal, reproducible example
https://codesandbox.io/p/devbox/vigilant-forest-7j45zd?file=%2Fsrc%2Findex.tsx%3A58%2C1
Steps to reproduce
See that usePosts
and usePosts2
both have a data
type claiming to be number
, but are instead a Post
at runtime.
Examples:
function usePosts() {
return useQuery<Post[], Error, number, string[]>({
queryKey: ['posts'],
queryFn: async (): Promise<Array<Post>> => {
const response = await fetch(
'https://jsonplaceholder.typicode.com/posts'
);
return await response.json();
},
});
}
function usePosts2(): UseQueryResult<number, Error> {
return useQuery({
queryKey: ['posts'],
queryFn: async (): Promise<Array<Post>> => {
const response = await fetch(
'https://jsonplaceholder.typicode.com/posts'
);
return await response.json();
},
});
}
Expected behavior
I expect to get a type-error, because I haven't specified a select
function and TQueryFnData
does not equal TData
, therefore we have easy, uncaught runtime breaks.
Potential solutions:
- Using
NoInfer
on theUseQueryResult<NoInfer<TData>, TError>
, will at least preventusePost2
issue - We should be able to write a conditional type by using additional overloads (untested/hypothetically) to narrow on the condition where we have a
select
specified.
type UseQueryOptionsWithSelect = ...
type UseQueryOptionsWithoutSelect = ...
declare function useQuery<TQueryFnData = unknown, TError = DefaultError, TData = TQueryFnData, TQueryKey extends QueryKey = QueryKey>(options: UseQueryOptionsWithoutSelect<TQueryFnData, TError, TData, TQueryKey>, queryClient?: QueryClient): UseQueryResult<TQueryFnData extends TData ? NoInfer<TData> : never, TError>;
declare function useQuery<TQueryFnData = unknown, TError = DefaultError, TData = TQueryFnData, TQueryKey extends QueryKey = QueryKey>(options: UseQueryOptionsWithSelect<TQueryFnData, TError, TData, TQueryKey>, queryClient?: QueryClient): UseQueryResult<NoInfer<TData>, TError>;
How often does this bug happen?
None
Screenshots or Videos
No response
Platform
N/A
Tanstack Query adapter
None
TanStack Query version
5.66.0
TypeScript version
5.7.2
Additional context
No response