lukemorales / query-key-factory

A library for creating typesafe standardized query keys, useful for cache management in @tanstack/query
https://www.npmjs.com/package/@lukemorales/query-key-factory
MIT License
1.16k stars 32 forks source link

Type inference is not compatible with query #59

Closed baeharam closed 1 year ago

baeharam commented 1 year ago

@lukemorales Thanks for your great library!

I'm using this package to manage query keys effectively, but when I have to use custom options to inject useQuery or something like this, it is not "easily" compatible with types of @tanstack/react-query.

For example,

const useCustomQuery = (options?: UseQueryOptions<DataType>) => {
  // TYPE ERROR
  return useQuery({ ...options, ...queryKey.someQuery });
}

In above case, type error is occurred so I have to use specify query key type of this package.

const useCustomQuery = (options?: UseQueryOptions<DataType, unknown, unknown, typeof queryKey.someQuery.queryKey>) => {
  return useQuery({ ...options, ...queryKey.someQuery });
}

It solves type issue, but I think is awkward to specify 3 types with UseQueryOptions. I want more simple solution like below.

const useCustomQuery = (options?: UseQueryOptions<typeof queryKey.someQuery>) => {
  return useQuery({ ...options, ...queryKey.someQuery });
}

It may be related to issue #48 and #40. I hope it could be solved elegantly :) Thanks for reading! 😄

[Reproduction]

스크린샷 2023-04-30 오후 10 13 07

https://codesandbox.io/s/type-error-of-query-key-factory-49so1x?file=/src/App.tsx

joeyfigaro commented 1 year ago

Any movement on this? @baeharam how have you worked around this issue? I'm having the same problem.

lukemorales commented 1 year ago

@joeyfigaro @baeharam sorry, I completely missed this issue with a lot that's going on my personal life. I'll take a look and try to come up with a fix in the next upcoming days

lukemorales commented 1 year ago

@joeyfigaro @baeharam just released v1.3.0 that exposes a new type TypedUseQueryOptions for you to use in these cases:

import { TypedUseQueryOptions } from '@lukemorales/query-key-factory'

const useCustomQuery = (options?: TypedUseQueryOptions<typeof queryKey.someQuery>) => {
  return useQuery({ ...options, ...queryKey.someQuery });
}

As a bonus tip: since you're allowing all options (and possibly the select option that will change the data type), always create your hooks with a generic:

import { TypedUseQueryOptions } from '@lukemorales/query-key-factory'

const useCustomQuery = <Data = SomeQueryResult>(options?: TypedUseQueryOptions<typeof queryKey.someQuery, Data>) => {
  return useQuery({ ...options, ...queryKey.someQuery });
}

// in your component

const { data } = useCustomQuery()
//       ^? SomeQueryResult

const { data } = useCustomQuery({ select: data => data.name })
//       ^? string
baeharam commented 1 year ago

@lukemorales Thanks for your solution, but it has 2 limitations.

  1. I have to specify type of "selector". Originally, query infers selected data type with select field. But TypedUseQueryOptions needs "selector" data type with generic. In practice, selected type is too complex so defining selector type everytime is very nervous.

  2. When querykey object is generated by function, it causes type error like below.

const queryKey = createQueryKeys("test", () => ({
  test1Query: (params: SomeParamsType) => ({ queryKey: ..., queryFn: ... })
});

In above case, I cannot use TypedUseQueryOptions.

These 2 problems are very important so I hope it could be solved ASAP. Thanks! 😄

lukemorales commented 1 year ago

@baeharam can you explain exactly why 1 is very nervous? Even with UseQueryOptions, if you don't specify a generic for the selected data at the function level, when you use select in your options param, it will not reflect that you're returning a different shape than the query function return type.

As for number 2, that's just typescript syntax: TypedUseQueryOptions<ReturnType<typeof queryKey['test1Query']>>

lukemorales commented 1 year ago

I can see if providing a function that returns queryKey and queryFn for 2 it will also handle internally, but that's improved DX, not a problem at all, that's just using plain typescript

lukemorales commented 1 year ago

@baeharam can you explain exactly why 1 is very nervous? Even with UseQueryOptions, if you don't specify a generic for the selected data at the function level, when you use select in your options param, it will not reflect that you're returning a different shape than the query function return type.

As for number 2, that's just typescript syntax: TypedUseQueryOptions<ReturnType<typeof queryKey['test1Query']>>

@baeharam here's what I'm talking about: https://codesandbox.io/s/type-error-of-query-key-factory-forked-38yxsq?file=/src/App.tsx

You can see on line 34 that typescript is complaining about the different data being returned on select prop and if you hover over selectedData on line 33, you will see that the type is still TestResponse

baeharam commented 1 year ago

@lukemorales

Sry, I misunderstood that original selector type inference of query. Your answer is right for number 2. But as for number 1, Returntype<typeof queryKey.testQuery> causes type error 😢

image

Reproduction: https://codesandbox.io/s/type-error-of-query-key-factory-forked-j32rwr?file=/src/App.tsx

If I did something wrong, plaese tell me. Thanks!

lukemorales commented 1 year ago

@baeharam your example has a small mistake on the queryFn declaration because it's returning directly the fetch invocation instead of a function that returns the promise. However, even fixing that, typescript was understanding the types were too strict and not compatible when the query keys were dynamic generated for some unknown reason 🤷🏻 Because of that, I just released a new patch with loosen types (and you don't need to use ReturnType anymore) that should fix the issue

baeharam commented 1 year ago

@lukemorales Thanks for your quick work. It works very well 😄

But, I need this kind of utility type with useInfiniteQuery and useMutation 😢 Sorry for bothering...

lukemorales commented 1 year ago

@baeharam I can work on the useInfiniteQuery options today, but for useMutation I'm gonna hold off for now because createMutationKeys type inference is broken for now so I'm not pursuing any improvements there before I can fix it

baeharam commented 1 year ago

@lukemorales Oh, I understood! Then, can you add type for useInfiniteQuery options?

joeyfigaro commented 1 year ago

@lukemorales I completely missed your responses here–thank you so much for the quick turnaround on this. I'm going to give the new release a shot this evening! Do you have a tip jar anywhere for your work?