Skip to content

broken typescript types: tanstack/query queryOptions are not compatible with queryCollectionOptions #1279

@valerii15298

Description

@valerii15298
  • 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.

Image

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:
Image

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.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions