Skip to content

setQueryData updater loses discriminated-union fields when spreading inferred NoInfer data #11794

Description

@ryoid

Describe the bug

With @tanstack/react-query 5.104.0, an updater passed to setQueryData(queryOptions(...).queryKey, updater) fails when it narrows a discriminated union and spreads the narrowed value. The tagged key supplies the correct data type, but the inferred updater parameter is wrapped in NoInfer<Data>. The spread loses the variant-specific fields.

Explicitly annotating the updater parameter as Data | undefined makes the same update compile. This appears related to #11018, but affects the QueryClient cache updater rather than the useQuery result.

Minimal reproduction

import { QueryClient, queryOptions } from '@tanstack/react-query'

type Data =
  | { kind: 'a'; value: number; requiredA: number }
  | { kind: 'b'; value: number; requiredB: number }

const query = queryOptions({
  queryKey: ['example'],
  queryFn: async (): Promise<Data> => ({ kind: 'a', value: 1, requiredA: 1 }),
})
const client = new QueryClient()

// Correctly inferred from the tagged key.
const inferred: Data | undefined = client.getQueryData(query.queryKey)

// Fails with TS2345.
client.setQueryData(query.queryKey, (prev) => {
  if (!prev || prev.kind !== 'a') return prev
  return { ...prev, value: prev.value + 1 }
})

// The same update passes with an explicit parameter annotation.
client.setQueryData(query.queryKey, (prev: Data | undefined) => {
  if (!prev || prev.kind !== 'a') return prev
  return { ...prev, value: prev.value + 1 }
})

Steps to reproduce

Typecheck the example with TypeScript 5.8.3:

tsc --noEmit --strict --skipLibCheck --target es2023 --moduleResolution bundler --module esnext repro.ts

The unannotated updater fails with a return type containing { value: number; kind: "a" | "b" }, missing requiredA and requiredB. The annotated updater compiles.

Expected behavior

After prev.kind !== 'a' returns, spreading prev should preserve kind: 'a' and requiredA. The documented query-options key inference should allow this update without repeating the data type on the parameter.

Versions

  • @tanstack/react-query: 5.104.0
  • @tanstack/query-core: 5.104.0
  • TypeScript: 5.8.3
  • Platform: macOS; compile-time only

Additional context

The reproducer uses no application-specific wrappers, key registration, or casts. The reverse-inference guard should remain intact; this report is about preserving normal union narrowing and spread behavior while retaining that guard.

Activity

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