Skip to content

feat(useMutationState): add TMutation generic to propagate mutation types into select callback#10790

Open
n-satoshi061 wants to merge 3 commits into
TanStack:mainfrom
n-satoshi061:feat/useMutationState-generic-TMutation
Open

feat(useMutationState): add TMutation generic to propagate mutation types into select callback#10790
n-satoshi061 wants to merge 3 commits into
TanStack:mainfrom
n-satoshi061:feat/useMutationState-generic-TMutation

Conversation

@n-satoshi061
Copy link
Copy Markdown

@n-satoshi061 n-satoshi061 commented May 25, 2026

Summary

Fixes #9825

The mutation argument in the select callback of useMutationState always had type Mutation<unknown, Error, unknown, unknown>, losing any specific type information and requiring manual casts.

Before

// ❌ mutation argument in select is always Mutation<unknown, Error, unknown, unknown>
// → manual cast required to access typed state
const result = useMutationState({
  filters: { mutationKey: ['KEY'] },
  select: (mutation) =>
    mutation.state as MutationState<MyData, MyError, MyVars>,
})

After

// ✅ specifying TMutation resolves the type inside select
const result = useMutationState<
  MutationState<MyData, MyError, MyVars>,
  Mutation<MyData, MyError, MyVars>
>({
  filters: { mutationKey: ['KEY'] },
  select: (mutation) => mutation.state, // mutation is Mutation<MyData, MyError, MyVars>
})

Changes

  • Added TMutation extends Mutation<any, any, any, any> = Mutation generic to MutationStateOptions and useMutationState in all framework adapters:
    • react-query
    • vue-query
    • solid-query
    • svelte-query
    • preact-query
    • lit-query
  • Added type test to react-query verifying that TMutation generics propagate correctly into the select callback
  • The second generic parameter defaults to Mutation so existing usage is fully backward compatible

Type safety note

TMutation is a caller-side assertion. mutationCache.findAll() always returns the base Mutation type, so the cast mutation as unknown as TMutation is type-only and erased at runtime. It is the caller's responsibility to ensure TMutation matches the actual mutations in the cache (e.g. by also specifying mutationKey in filters).

Test plan

  • npx nx run @tanstack/react-query:test:lib — all runtime tests pass
  • npx nx run @tanstack/react-query:test:types — type tests pass on TS 5.4 through 6.0
  • New type test added: useMutationState.test-d.tsx — "should propagate TMutation generics into select callback"

🤖 Generated with Claude Code

Add a second generic parameter `TMutation extends Mutation<any, any, any, any>`
to `MutationStateOptions` and `useMutationState` across all framework adapters.
This lets callers type the `select` callback's mutation argument without manual casting.

Fixes: users having to write `mutation.state as MutationState<MyData, MyError, MyVars>`
when filtering by a known mutationKey.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai Bot commented May 25, 2026

📝 Walkthrough

Walkthrough

Adds a TMutation generic to MutationStateOptions and threads it through useMutationState across frameworks (React, Vue, Solid, Svelte, Preact, Lit). Select callbacks now receive a typed TMutation; implementations cast cached mutations to TMutation. A changeset and type-level test were added.

Changes

useMutationState TMutation Generic Propagation

Layer / File(s) Summary
Type Contract: MutationStateOptions Generalization
.changeset/usemutationstate-generic-tmutation.md, packages/svelte-query/src/types.ts, packages/vue-query/src/useMutationState.ts, packages/lit-query/src/useMutationState.ts, packages/preact-query/src/useMutationState.ts
MutationStateOptions gains a second generic parameter TMutation (defaulting to Mutation). The select callback is now typed to accept the parameterized mutation type instead of an untyped Mutation.
Hook Implementations with TMutation Threading
packages/react-query/src/useMutationState.ts, packages/lit-query/src/useMutationState.ts, packages/preact-query/src/useMutationState.ts, packages/solid-query/src/useMutationState.ts, packages/svelte-query/src/useMutationState.svelte.ts, packages/vue-query/src/useMutationState.ts
Hook functions, controllers, and helpers are updated to accept and propagate TMutation. Cached Mutation objects are cast to TMutation before being passed to select; runtime subscription and state-update behavior is unchanged.
Type-Level Test Verification
packages/react-query/src/__tests__/useMutationState.test-d.tsx
Adds a type-only test asserting that useMutationState generics (TData, TError, TVariables) propagate into the select callback and the returned array is Array<MutationState<MyData, MyError, MyVariables>>.

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~20 minutes

Poem

🐰
I hopped through generics, soft and spry,
Threaded TMutation so types fly high.
No more casts in select so neat,
Mutation shapes now fit complete.
From React to Svelte, the typing's right—hip, hop, delight!

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 7.14% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Title check ✅ Passed The title clearly and concisely describes the main change: adding a TMutation generic to useMutationState to propagate mutation types into the select callback.
Linked Issues check ✅ Passed The PR fully addresses issue #9825 by adding TMutation generic parameter to MutationStateOptions and useMutationState across all framework adapters, enabling type propagation into select callbacks and eliminating the need for manual casts.
Out of Scope Changes check ✅ Passed All changes are directly related to the stated objective of adding TMutation generic support to useMutationState across framework adapters; no out-of-scope modifications detected.
Description check ✅ Passed The pull request description is comprehensive and well-structured, covering all key sections including changes, test plan, and type safety considerations.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

n-satoshi061 and others added 2 commits May 25, 2026 20:30
Adding a new generic parameter to a public API is a new feature,
not a bug fix. The feat() prefix in the description already signals this.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…pters

Documents the caller-side assertion semantics of TMutation so users
understand the mutation cache stores base Mutation types and the
generic is their responsibility to keep consistent with filters.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

useMutationState does not propagate generics into select callback (type inference lost)

1 participant