[vue-query] useQuery rejects spreading a queryOptions() result + override since v5.98.0 · Issue #10904 · TanStack/query · GitHub
Skip to content

[vue-query] useQuery rejects spreading a queryOptions() result + override since v5.98.0 #10904

Open

Description

@lasficw

Describe the bug

Since @tanstack/vue-query v5.98.0, the documented pattern of spreading a queryOptions(...) result into useQuery and overriding a non-data-shaping option (e.g. enabled) no longer type-checks. Passing the same options object directly works; only the spread + override form fails.

This is a downstream continuation of #10525. That fix made queryOptions({ queryKey: computed(...) }) type-check again, but the branded queryKey (DataTag<...>) is still inconsistent with the other option properties once the result is spread into a fresh object literal, so useQuery inference fails with TS2769: No overload matches this call.

The most revealing part of the error cascade:

Types of property 'staleTime' are incompatible.
  ...
  Type 'readonly ["activity", "detail", string | null | undefined]' is not assignable to type
    '{ ...; [dataTagErrorSymbol]: Error; }'.

Your minimal, reproducible example

import { queryOptions, useQuery } from "@tanstack/vue-query"
import { computed, type MaybeRefOrGetter, toValue } from "vue"

const fooQueryOptions = (id: MaybeRefOrGetter<string | null>) =>
  queryOptions({
    queryKey: computed(() => ["foo", toValue(id)] as const),
    queryFn: async () => {
      const v = toValue(id)
      return v ? { id: v } : null
    },
  })

// ✅ OK — passed directly
useQuery(fooQueryOptions("1"))

// ❌ TS2769 since 5.98.0 (works on 5.97.0) — spread + override
useQuery({
  ...fooQueryOptions("1"),
  enabled: () => true,
})

Steps to reproduce

  1. Install @tanstack/vue-query@5.101.0 (or any version from 5.98.0 onward).
  2. Define a queryOptions(...) factory with a computed queryKey.
  3. Spread its result into useQuery({ ...opts, enabled: () => true }).
  4. Run tsc --noEmit / vue-tsc.
  5. TypeScript reports TS2769: No overload matches this call, bottoming out at the queryKey missing the dataTagSymbol / dataTagErrorSymbol brand.

Note: useQuery(opts) (passing the same options directly, without spreading or overriding) type-checks fine — only the spread + override form fails.

Expected behavior

Spreading a queryOptions(...) result and overriding enabled (or other non-data-shaping options) should type-check, as shown in the docs and as it did on 5.97.0:
https://tanstack.com/query/latest/docs/framework/vue/guides/query-options

How often does this bug happen?

Every time

Screenshots or videos

No response

Platform

  • TanStack Query version: broken on 5.98.0 – 5.101.0; last working 5.97.0
  • TypeScript version: 5.8+
  • Vue version: 3.5+

Additional context

Same class of regression as #10458 (fixed enabled) and #10525 (fixed queryKey on queryOptions), but it surfaces at the useQuery call site when spreading a queryOptions(...) result and overriding a property. Pinning to 5.97.0 produces zero type errors; any of 5.98.0 – 5.101.0 reproduces it.

Activity

  1. barry166 commented on Jun 28, 2026

    @barry166

    This reads like the spread is breaking the relationship between the branded queryKey and the rest of the queryOptions() result, rather than an enabled-specific issue. The smallest useful coverage is probably the exact Vue shape here: computed query key, queryOptions(...), then { ...opts, enabled } at the useQuery call site.

  2. alexmccabe commented on Jun 29, 2026

    @alexmccabe

    This can be resolved, at least from my local testing, by using the Vue exported types for MaybeRefOrGetter, Ref and UnwrapRef. I copied the queryOptions source into our repo, added the required type utils this library creates and I have no type issues.

    Things are portable again. I'm unsure of the reasoning why the library elected to create their own versions of these type utils, but I'm sure they had a good reason.

    Unfortunately, since these types are not exported, the TS compiler cannot craft the return type of the function correctly when using computed queryKeys. The DataTag type just fails.

    Likely if the composite: true option is turned off in your project, then you won't run into this issue, but if this value is on then TS must emit a .d.ts file for any helper.


    Edit: This just kicks the rock further down the road to a QueryBehaviour type 🙄

  3. WhoamiI00 commented on Aug 16, 2026

    @WhoamiI00

    Couldn't reproduce this on main (vue-query 5.101.4). Recording what I tried so it's either closeable or someone can sharpen the repro.

    All of these type-check with no errors, run as .test-d.ts against the repo's typecheck setup:

    // plain key, spread + override
    const options = queryOptions({ queryKey: key, queryFn: () => Promise.resolve(5) })
    useQuery({ ...options, enabled: false })
    
    // computed key + staleTime, spread + override
    const options = queryOptions({
      queryKey: computed(() => ['activity', 'detail', id.value] as const),
      queryFn: () => Promise.resolve({ n: 5 }),
      staleTime: 5000,
    })
    useQuery({ ...options, enabled: false })
    
    // with select
    queryOptions({ ..., select: (d: { n: number }) => d.n })  // spread + override: ok
    
    // curried factory, override with a computed
    const factory = (id: string) => queryOptions({ queryKey: computed(() => ['activity', 'detail', id] as const), queryFn: () => Promise.resolve(5), staleTime: 60_000 })
    useQuery({ ...factory('1'), enabled: computed(() => true) })

    Worth noting the asymmetry that looked like the cause is still there: queryOptions.ts special-cases queryKey as MaybeRefOrGetter<TQueryKey>, while useQuery's UseQueryOptions maps every property through MaybeRefDeep. I expected that to mangle the DataTag brand on spread, but it evidently doesn't — so whatever broke on 5.98.0 seems to have been resolved between then and 5.101.4 rather than by that difference.

    If it still fails for you on a current version, the TypeScript version and the exact queryOptions call would help — the error in the report points at staleTime, which I couldn't get to fail here.

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

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