-
Notifications
You must be signed in to change notification settings - Fork 173
Description
- I've validated the bug against the latest version of DB packages
Describe the bug
Result of queryOptions is not compatible with queryCollectionOptions
To Reproduce
Code to reproduce the behavior:
import { queryCollectionOptions } from "@tanstack/query-db-collection";
import { createCollection } from "@tanstack/react-db";
import { QueryClient, queryOptions } from "@tanstack/react-query";
const queryClient = new QueryClient();
const queryKey = ["numbers"];
const queryFn = () =>
Promise.resolve([
{ id: 1, value: "one" },
{ id: 2, value: "two" },
]);
const opts = queryOptions({ queryKey, queryFn });
const collection = createCollection(
queryCollectionOptions({
...opts,
// uncomment these to make it work
// queryKey,
// enabled: true,
// queryFn: (ctx) => Promise.resolve(opts.queryFn!(ctx)),
queryClient,
getKey(item) {
return item.id;
},
}),
);Expected behavior
There should be no typescript errors with the code above.
Motivation
A lot of tools already provide options using queryOptions utility and would be great if it was compatible with tanstack/db.
I specifically use oRPC and need to do these workarounds in order to make things works :(
Latest dependencies on time of this issue creation:
dependencies:
@tanstack/query-db-collection 1.0.24
@tanstack/react-db 0.1.71
@tanstack/react-query 5.90.21
Additional Context and debugging
While testing here is additional context when testing each key compatibility, basically only commenting one line from the next working code which uses workaround:
import { queryCollectionOptions } from "@tanstack/query-db-collection";
import { createCollection } from "@tanstack/react-db";
import { QueryClient, queryOptions } from "@tanstack/react-query";
const queryClient = new QueryClient();
const queryKey = ["numbers"];
const queryFn = () =>
Promise.resolve([
{ id: 1, value: "one" },
{ id: 2, value: "two" },
]);
const opts = queryOptions({ queryKey, queryFn });
const collection = createCollection(
queryCollectionOptions({
...opts,
queryKey,
enabled: true,
queryFn: (ctx) => Promise.resolve(opts.queryFn!(ctx)),
queryClient,
getKey(item) {
return item.id;
},
}),
);In the above code there will be errors when commenting out one of: queryKey | enabled | queryFn
1. queryKey type inconsistency with symbol keys
For queryKey the problem is that queryOption returns it with the type of:
(property) queryKey: string[] & {
[dataTagSymbol]: {
id: number;
value: string;
}[];
[dataTagErrorSymbol]: Error;
}
And that is fine for passing queryKey itself because queryCollectionOptions expects it to be of type:
string[] | ((opts: LoadSubsetOptions) => string[])
So it is assignable(broader type is assignable to narrower type)
But queryKey is also used in queryFn as argument and in this case it is not assignable because assignability works in opposite way:
For example string is assignable to string | number but (a: string) => void is not assignable to (a: string | number) => void
Solution would be for queryCollectionOptions to include those symbols types for queryKey
2. Types of property enabled are incompatible.
queryCollectionOptions expects enable to be boolean | undefined but enabled returned from queryOptions is some complex generic type(I did not dive deeper into this 😅)
3. Types of property queryFn are incompatible.
First error is that queryFn is optional when returned from queryOptions.
When making it: queryFn: opts.queryFn!, the error becomes this:

queryFn type from queryOptions is:
Promise<{ id: number; value: string; }[]> | { id: number; value: string; }[]
But queryCollectionOptions always expects the queryFn to return a promise. So workaround is as I did: queryFn: (ctx) => Promise.resolve(opts.queryFn!(ctx)),
Solution would be to allow it to accept a raw data without a Promise.