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

[vue-query] Type error: queryOptions return type only contains the queryKey and initialData properties #7892

Open
basuneko opened this issue Aug 13, 2024 · 3 comments

Comments

@basuneko
Copy link

Describe the bug

There seems to be a discrepancy between the return type of queryOptions in vue query and react query adapters.

const options = queryOptions({
  queryKey: ['groups'],
  queryFn: () => []
})

useQuery(options)

options.queryFn

The actual options object seems to be okay: useQuery, fetchQuery, etc functions accept it and work just fine. But trying to access queryFn, placeholderData, etc throws a TS error

Property 'queryFn' does not exist on type 'UndefinedInitialQueryOptions<Group[], Error, Group[], string[]> & { queryKey: DataTag<string[], Group[]>; }'.

In fact, only queryKey and initialData properties are suggested in autocomplete. With react-query, all of the query options (queryFn, placeholderData, etc) are suggested.

Your minimal, reproducible example

https://www.typescriptlang.org/play/?#code/JYWwDg9gTgLgBAbzgRwK4FMoE8DyYbAQB2AznAL5wBmUEIcA5AAIwCGpbAxgNYD0U6VpxgBaNJiwMAsAChQkWIhQZseAsTKsyANwwBFFbnyFSAGjioS6AxLha4u9AFUrN7OcvXDAYQA2wdCJ4Sho6RhZ2Ei4+RzFDaTlwaHhWAA9CMlD6BjSMhNkYLDB0OABxWlQwOABeJWAAEwAuOCJUEAAjTApZWU4NeCp0GE4AC3KISrJagAoASmaABVoQYCsAHnHKgG0AXQA+GoPciBIAOgBzIemGXnOKsBIGWdOYEcDpgRJIUhLqg8-vlZTvVWGxZj0ZBC+hw4AIhDA1CYpsoJIiNNMELI4NiUdgANLoLDNLYMO4TB4MHamLE48TYABiRGag2GY3uJFk5HBkJkcOEaNIsl4vDgAD0APwQvkI4waU50rCMoUinGq1US2TSgVnMC+IToEYQXz1TAAEVBrGVautYslPOhUQcGG1NSdXlRstIGJp2IVBKJcBJZMmlOpMlVCsZzKGo02D053IhskcLnd2Gmjm1iZkDvgnH8gXgtU8biwfgCQTmvQLQVOLNGpYzzs9JGzmZbVo1Mnb6lI8sMSpkwptaq7PaRp11+sNxrNFqtI5xXd6-Td2oA6sBXgBJIhb4CsXzmtiuxyl7Xe8O0wz+4mk9mhn24xVM6gxtnkkhh1XAPcEQ-HqwxI7AmELjhom47n+B5Hha-YSIOw6Ltiy4yEAA

Steps to reproduce

  1. Define query options using the queryOptions helper (with or without the initial data)
  2. Try to access queryOptions.queryFn

Expected behavior

I expected queryFn to be available but I'm getting a TS error: Property 'queryFn' does not exist on type UndefinedInitialQueryOptions<...>

How often does this bug happen?

Every time

Screenshots or Videos

Screenshot 2024-08-13 at 14 59 51

Platform

Chrome 127.0.6533.100
Mac OS 10.15.7

Tanstack Query adapter

vue-query

TanStack Query version

v5.51.21

TypeScript version

v5.5.4

Additional context

No response

@basuneko basuneko changed the title Type error: queryOptions return type only contains the queryKey and initialData properties [vue-query] Type error: queryOptions return type only contains the queryKey and initialData properties Aug 13, 2024
@TkDodo
Copy link
Collaborator

TkDodo commented Aug 19, 2024

the order of overloads seems to be reversed in the vue implementation. Should be an easy fix to just switch the order of implementation. Would you like to contribute that?

@basuneko
Copy link
Author

basuneko commented Aug 24, 2024

TL;DR looks like my original issue is caused by the fact that UseQueryOptions is a MaybeRef, and it needs to be unwrapped to access the fields safely.
But, queryOptions returns a UseQueryOptions & DataTag. If passed a Ref it would lead to a Ref & DataTag which is incorrect. The type would be accepted by useQuery but it can break in runtime if used with an object spread operator.


@TkDodo thanks! I've made some progress, and I think it's a bigger issue with.

It looks like the problem is that UseQueryOptions in vue-query is a MaybeRef - makes sense since useQuery accepts a Ref/ComputedRef and, I’m guessing, queryOptions was extracted out of it. If you normalise the options you do get all the expected properties e.g. const rawOptions = unref(queryOptions(…)).queryFn.

But you can get a type mismatch if you pass a ref:

const options = queryOptions(
  computed(() => {
    queryKey: ['bacon'],
    queryFn: () => Promise.resolve(5)
  })
)

The inferred type of options is essentially UseQueryOptions & DataTag while the actual type is more like MaybeRef<UnwrapRef<UseQueryOptions> & DataTag>.

This will still work with useQuery in simple cases but it will backfire if you try to change the options

const query = useQuery(refOptions) // This works okay
const query = useQuery({
  …refOptions,
  select: (data) => data + 1
}) // This passes tsc but breaks in runtime because you cannot spread a Ref like that.

So the question is, can queryOptions distinguish between a Ref argument and a plain object and return the appropriate type?
And if not, would this MaybeRef<UnwrapRef<UseQueryOptions> & DataTag> be the appropriate way to type queryOptions?

Maybe @DamianOsipiuk can weigh in? Since this now affects runtime, I’ve created a repo.

@DamianOsipiuk
Copy link
Contributor

I played around a bit with this, but could not solve it completely.
Making return type base on input type and making inference work properly might require some existing types rework.

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

No branches or pull requests

3 participants