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

Typescript issue with queryOptions and usePrefetchQuery #7560

Closed
robcaldecott opened this issue Jun 13, 2024 · 2 comments
Closed

Typescript issue with queryOptions and usePrefetchQuery #7560

robcaldecott opened this issue Jun 13, 2024 · 2 comments

Comments

@robcaldecott
Copy link

Describe the bug

I am attempting to add a custom usePrefetchQuery hook to my app based on the docs here:

https://tanstack.com/query/latest/docs/framework/react/guides/prefetching

I am also using the query factory pattern where I have an object containing query data wrapped using queryOptions. The problem is passing one of these query options objects into my hook is causing a TS issue unless I force a cast to either UseQueryOptions or EnsureQueryDataOptions. However, calling ensureQueryData and directly passing in a queryOptions object works OK.

import { EnsureQueryDataOptions, UseQueryOptions, queryOptions, useQueryClient } from "@tanstack/react-query";

const factory = {
  example: () => queryOptions({
    queryKey: ["hello"],
    queryFn: () => Promise.resolve("hello"),
  })
}

function usePrefetchQuery(options: UseQueryOptions) {
  const queryClient = useQueryClient();

  if (!queryClient.getQueryState(options.queryKey)) {
    queryClient.ensureQueryData(options).catch(() => {});
  }
}

// Error
usePrefetchQuery(factory.example());

// Works
usePrefetchQuery(factory.example() as UseQueryOptions);

// Works
usePrefetchQuery(factory.example() as EnsureQueryDataOptions);

// Works!
const queryClient = useQueryClient()

queryClient.ensureQueryData(queryOptions({
  queryKey: ["hello"],
  queryFn: () => Promise.resolve("hello"),
}));

The error looks like this:

Argument of type 'UseQueryOptions<string, Error, string, string[]> & { initialData?: undefined; } & { queryKey: DataTag<string[], string>; }' is not assignable to parameter of type 'UseQueryOptions<unknown, Error, unknown, QueryKey>'.
  Types of property 'staleTime' are incompatible.
    Type 'StaleTime<string, Error, string, string[]> | undefined' is not assignable to type 'StaleTime<unknown, Error, unknown, QueryKey> | undefined'.
      Type '(query: Query<string, Error, string, string[]>) => number' is not assignable to type 'StaleTime<unknown, Error, unknown, QueryKey> | undefined'.
        Type '(query: Query<string, Error, string, string[]>) => number' is not assignable to type '(query: Query<unknown, Error, unknown, QueryKey>) => number'.
          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[]'.(2345)
(property) example: () => UseQueryOptions<string, Error, string, string[]> & {
    initialData?: undefined;
} & {
    queryKey: DataTag<string[], string>;
}

I guess the type I am using for the hook options is incorrect but I am struggling to figure out what to use instead of UseQueryOptions.

Your minimal, reproducible example

https://www.typescriptlang.org/play/?#code/JYWwDg9gTgLgBAbzgUQHYGcCuUCmBFTHKATwBEBDGcgeTBmAgwBo4BVdfQk2+x9FgI5diPBsziYOBIsQDCAG2A5U8AL5wAZlAgg4AIgACVDFQDGAawD0ucqZgBaITL0BuAFBvTfeBtsxoxHAAvIhucHA4AB7k4PI4AFxwABQAlMEAfHBO3HRi6EkIYeFZwgDSOMSJANp6ABY48vIQegC6TEXh2cQAYqiJqRlwAAraIMAcAHS46BDyAG44SXUNTXop7eGqKW6qHhqYqHZiEhwjOBo4MKa10iRJELl8ieycMqJ8aYXhXiYlMgpKFTBE6vEgA5QwVLuIrADTJACEXXBKgmAHNLrdiABlKgwRYPXgYCZdcrEFKfDp-MGKCETZRYXCYihUe6PDApCamSjXJIDIKZJBbdybHYeSyWFBQbRQNySHBnC5XG7CJK+OwBOnRWKLcnQ8VwADq0HM6Flp1wiuumNVfg1URiYDiA3I6DYUmE73ZeolRqgJrN8otlytKrV-hImodTrSLpQGGwoLIlBobPQKW9huN6Hhnm8VLkNKBITlmORkO2biRhZgdPjjOEzPISS6nvyX3zpOqy0azTaRS6vX6aX5w1G4xwUxwM3mi27q3WO11biAA

Steps to reproduce

See TS playground link

Expected behavior

Passing queryFactory functions into ensureQueryData should be possible without TS errors.

How often does this bug happen?

Every time

Screenshots or Videos

No response

Platform

  • MacBook
  • Chrome

Tanstack Query adapter

react-query

TanStack Query version

5.40.1

TypeScript version

5.3.3

Additional context

Given how use usePrefetchQuery is, it would be very useful to know how to make it play nicely with TS and the query factory pattern in general.

@mitchell-up
Copy link

You should assign generics to infer options correctly.

If you don't assign generics, the type of options in usePrefetchQuery will be:

UseQueryOptions<unknown, Error, unknown, QueryKey>

but the type of factory.example() is:

UseQueryOptions<string, Error, string, string[]> & {
    initialData?: undefined;
} & {
    queryKey: DataTag<string[], string>;
}

You can see the generics are completely different each other.

So you can fix the problem as follows:

function usePrefetchQuery<
  TQueryFnData = unknown,
  TError = DefaultError,
  TData = TQueryFnData,
  TQueryKey extends QueryKey = QueryKey,
>(options: UseQueryOptions<TQueryFnData, TError, TData, TQueryKey>) {
  const queryClient = useQueryClient()

  if (!queryClient.getQueryState(options.queryKey)) {
    queryClient.ensureQueryData(options).catch(() => {})
  }
}

// I will work!
usePrefetchQuery(factory.example());

@robcaldecott
Copy link
Author

@mitchell-up oh thank you so much!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants