Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Unable to pass queryOptions to QueriesObserver (TypeScript error) #7446

Open
yannickcr opened this issue May 17, 2024 · 7 comments
Open

Unable to pass queryOptions to QueriesObserver (TypeScript error) #7446

yannickcr opened this issue May 17, 2024 · 7 comments
Labels

Comments

@yannickcr
Copy link

yannickcr commented May 17, 2024

Describe the bug

When passing options created with the queryOptions helper to QueriesObserver we got the following TypeScript error as it is unable to assign UseQueryOptions to QueryObserverOptions:

Type 'UseQueryOptions<string, Error, string, string[]> & { initialData?: undefined; } & { queryKey: DataTag<string[], string>; }' is not assignable to type 'QueryObserverOptions<unknown, Error, unknown, unknown, QueryKey, never>'.
  Types of property 'refetchInterval' are incompatible.
    Type 'number | false | ((query: Query<string, Error, string, string[]>) => number | false | undefined) | undefined' is not assignable to type 'number | false | ((query: Query<unknown, Error, unknown, QueryKey>) => number | false | undefined) | undefined'.
      Type '(query: Query<string, Error, string, string[]>) => number | false | undefined' is not assignable to type 'number | false | ((query: Query<unknown, Error, unknown, QueryKey>) => number | false | undefined) | undefined'.
        Type '(query: Query<string, Error, string, string[]>) => number | false | undefined' is not assignable to type '(query: Query<unknown, Error, unknown, QueryKey>) => number | false | undefined'.
          Types of parameters 'query' and 'query' are incompatible.
            Type 'Query<unknown, Error, unknown, QueryKey>' is not assignable to type 'Query<string, Error, string, string[]>'.
              Types of property 'queryKey' are incompatible.
                The type 'QueryKey' is 'readonly' and cannot be assigned to the mutable type 'string[]'.

Your minimal, reproducible example

https://www.typescriptlang.org/play/?#code/JYWwDg9gTgLgBAbzgRQK4FMoE8DCAbYdAOxgBoUNsB5AIwGdMA3TctTQu2hqZqcgR0pYqYGMAhE6cAL5wAZlAgg4AcgACMAIaStAYwDWAeijpNumAFpBmLCoBQd3RLrxr2fIRJwAvHCLoAdwobD2IYAAoASgBuBycdOBh0FzZsHzg3YVFxSXCEOzgMoQBpdCwALjgAbRUQW3IVfTKVAF1SAqKbADEiSs06LCJdOCifAD5VOrhGTTwMFXbpSLjneDqu4CgXLiZMdP8g1OF6XahwzNCSciSUoRiOwsfCx1W4OoBldHiAEx2ePd8B2CUA4f145yElzI1RuMCOLXuTyRyJRqLR6IxhkMcAAenA6AALCCoPDfPwQeCYRRQFYJOoAFQJm1+J3+UH2gWBoNZ4IuBDC5Cq+UKmVKFWqtXqqiatjaHUyPT6AyGI0i40mWGms3miwR0SAA

Steps to reproduce

In the playground, see that QueryObserver accepts testQuery as options and that myFirstObserver type is correctly inferred to QueryObserver<string, Error, string, string, string[]>.

However QueriesObserver errors when passing testQuery as options. If we inline the object then we have no errors.

Also QueryObserverResult is not properly inferred (TData is always unknown) even when passing the options as an inline object (I can open another issue for this point if needed).

No issue at runtime, the code is working as expected.

Expected behavior

We should be able to pass the result of the queryOptions helper to QueriesObserver and have QueryObserverResult properly inferred.

Quoting the documentation on https://tanstack.com/query/latest/docs/reference/QueriesObserver#queriesobserver:

The options for the QueriesObserver are exactly the same as those of useQueries.

So, from what I understand, the queryOptions helper here should work too (and as you can see in the playground above, it actually works with QueryObserver, but not QueriesObserver).

How often does this bug happen?

Every time

Screenshots or Videos

No response

Platform

  • macOS
  • Chrome 124

Tanstack Query adapter

react-query

TanStack Query version

v5.36.2

TypeScript version

v5.4.5

Additional context

No response

@TkDodo
Copy link
Collaborator

TkDodo commented May 17, 2024

Not sure if it should work or if it works coincidentally. The queryOptions are from the react package, while the observers are re-exported from the core. There are no type parameters on QueriesObserver constructor, but there are on the QueryObserver.

types for useQueries are horrendously complex, and I don't want to bring them over to QueriesObserver. What you can try is if widening this type signature:

queries: Array<QueryObserverOptions>,

to

queries: Array<QueryObserverOptions<any, any, any, any>>,

works.

On the other hand, I don't think it's expected that this works; if you're only depending on the core, you don't have access to queryOptions, and if you use the react package, you likely shouldn't need to use the Observers directly :)

@TkDodo TkDodo added the types label May 17, 2024
@yannickcr
Copy link
Author

if you're only depending on the core, you don't have access to queryOptions, and if you use the react package, you likely shouldn't need to use the Observers directly :)

Sorry, I did not specify it in the initial issue, but I'm using both actually @tanstack/query-core and @tanstack/react-query 😉

In our case we can have the same requests triggered from a react component or outside of a component, and we try to share as much code as possible between the different methods.

Here's an example of what I'm are trying to accomplish (where I have 3 ways to fetch the same data: with a function, with a React hook or within an observable (I'm using RxJS).

import { QueryClient } from '@tanstack/query-core';
import { queryOptions, useQuery, QueryObserver } from '@tanstack/react-query';
import { Subject } from 'rxjs';

export const queryClient = new QueryClient();

// My shared query options
export const getMyDataQueryOptions = () =>
  queryOptions({
    queryKey: ['myData'],
    queryFn: async () => {
      // Fetch data
      // ...
      return 'myData';
    },
  });

// Fetch data outside of a component
export const getMyData = () => queryClient.fetchQuery(getMyDataQueryOptions());

// Fetch data inside of a component
export const useMyData = () => useQuery(getMyDataQueryOptions());

// Fetch data as an observable
let subject: Subject<string>;
export const $getMyData = () => {
  if (subject) {
    return subject;
  }
  subject = new Subject();

  const observer = new QueryObserver(queryClient, getMyDataQueryOptions());

  observer.subscribe(result => {
    if (!result.isSuccess) {
      return;
    }
    subject.next(result.data);
  });

  return subject;
};

(using useQuery and QueryObserver here for simplicity, but the same apply for useQueries/QueriesObserver)

As you can see being able to reuse getMyDataQueryOptions() in each method would be really useful in my case.

The queryOptions are from the react package

I didn't even notice it. Is there a reason for this? This function is also useful outside of React (see the getMyData function in the example above)

types for useQueries are horrendously complex

I can believe you on this 😅

I don't want to bring them over to QueriesObserver

When you say that is it because it is too complex and you do not have the time for this or because that something you do not want for some architectural reason? (just to know if you would be interested by a PR if I'm able to make this work 😇 )

@TkDodo
Copy link
Collaborator

TkDodo commented May 17, 2024

but I'm using both actually @tanstack/query-core and @tanstack/react-query

usually not necessary because @tanstack/react-query re-exports everything from the query-core.

I don't know rxjs much but are you unsubscribing the observer correctly?

Is there a reason for this?

The type are slightly different for each adapter

When you say that is it because it is too complex and you do not have the time for this or because that something you do not want for some architectural reason?

A bit of both probably 😂 . If the widening to <any, any, any, any> works, I'd accept a PR for that.

@yannickcr
Copy link
Author

usually not necessary because @tanstack/react-query re-exports everything from the query-core.

Noted 👍

I don't know rxjs much but are you unsubscribing the observer correctly?

No, but I do not want to unsubscribe (our project is a bit particular on this point, but I think this is not important for our problem here 🙂)

The type are slightly different for each adapter

That's a good explanation (it raise a lot of other questions, but I'll open a discussion because that's a bit out of scope here 😅).

A bit of both probably 😂 . If the widening to <any, any, any, any> works, I'd accept a PR for that.

Haha. Ok 😄. The widening to <any, any, any, any> works as it solve the TS error, but of course we miss the inference of the results. It's a good quick fix for now I suppose. I can submit a PR for this.

@TkDodo
Copy link
Collaborator

TkDodo commented May 17, 2024

ah, if we miss inference then it's no good :/

@yannickcr
Copy link
Author

By missing inference I mean that in the playground above the types of mySecondObserver and myThirdObserver will be QueriesObserver<QueryObserverResult[]>

(instead of QueriesObserver<[QueryObserverResult<string, Error>]> that would have been ideal but require more work)

@TkDodo
Copy link
Collaborator

TkDodo commented May 20, 2024

ah okay, then let's do that to make it at least passable ...

yannickcr added a commit to yannickcr/tanstack-query that referenced this issue May 29, 2024
Widen the `QueriesObserver` queries type so we can assign `UseQueryOptions` to `QueryObserverOptions`.

With this change we can now pass the result from the `queryOptions` helper to `QueriesObserver`.
yannickcr added a commit to yannickcr/tanstack-query that referenced this issue May 29, 2024
Widen the `QueriesObserver` queries type so we can assign `UseQueryOptions` to `QueryObserverOptions`.

The result from the `queryOptions` helper can now be passed to `QueriesObserver`.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

No branches or pull requests

2 participants