Skip to content

setQueryData: NoInfer loses discriminated-union members when spreading a narrowed updater value #11795

Description

@ryoid

Describe the bug

With @tanstack/react-query@5.104.0, setQueryData infers the correct data type from a queryOptions key, but narrowing and spreading that union inside the updater produces a false assignability error. Explicitly annotating the updater parameter as Data | undefined makes the same code compile.

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: ['probe'],
  queryFn: async (): Promise<Data> => ({ kind: 'a', value: 1, requiredA: 1 }),
});
const client = new QueryClient();

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

Run with TypeScript 5.8.3:

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

Actual behavior

TS2345: the callback return is inferred as NoInfer<Data> | { value: number; kind: 'a' | 'b' } | undefined. The spread loses the variant-specific required fields, so the returned object is rejected as Data.

Expected behavior

After the guard, spreading prev should preserve kind: 'a' and requiredA. The updater should compile without repeating the data type already encoded in the query key.

Workaround

Changing only (prev) to (prev: Data | undefined) compiles.

Versions and related context

  • @tanstack/react-query: 5.104.0
  • TypeScript: 5.8.3
  • Reproduces during typechecking; no browser or network calls needed.

Related: #11018 and #11245 address the useQuery return type. This report concerns the remaining NoInfer interaction in QueryClient.setQueryData updater parameters. The inferred key type itself is correct; the failure occurs during union narrowing/spread.

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