Skip to content

fix(types): add TMutation generic to useMutationState with caller-side assertion JSDoc#10870

Open
junjuny0227 wants to merge 2 commits into
TanStack:mainfrom
junjuny0227:fix/useMutationState-tmutation-jsdoc
Open

fix(types): add TMutation generic to useMutationState with caller-side assertion JSDoc#10870
junjuny0227 wants to merge 2 commits into
TanStack:mainfrom
junjuny0227:fix/useMutationState-tmutation-jsdoc

Conversation

@junjuny0227
Copy link
Copy Markdown
Contributor

@junjuny0227 junjuny0227 commented Jun 3, 2026

🎯 Changes

Closes #10792.

Problem

useMutationState uses as unknown as TMutation internally, which is a caller-side type assertion with no runtime validation. The safety warning for this existed only on the MutationStateOptions type parameter — a location users rarely hover over in their IDE. As confirmed in the issue comment via the TypeScript Language Service API, inline JSDoc on a type parameter is not surfaced at the call site. Only a @template tag in a function-level JSDoc block is shown on hover.

Solution

Two changes applied across all six adapters (react, preact, solid, vue, svelte, lit):

1. Add TMutation generic parameter

A MutationTypeFromResult<TResult> helper infers TMutation automatically when TResult is a typed MutationState, so the select callback receives the correctly-typed Mutation without any manual casting:

// Before — manual cast required
useMutationState<MutationState<MyData, MyError, MyVars>>({
  select: (mutation) => (mutation as Mutation<MyData, MyError, MyVars>).state.data,
})

// After — type is inferred automatically
useMutationState<MutationState<MyData, MyError, MyVars>>({
  select: (mutation) => mutation.state.data,
})

The default value of TMutation falls back to the base Mutation type when TResult is not a typed MutationState, preserving full backward compatibility.

2. Surface the caller-side assertion warning via @template JSDoc

/**
 * @template TMutation - Narrows the type of the `mutation` argument passed to
 * `select`. This is a caller-side assertion — the mutation cache stores
 * mutations as the base `Mutation` type, so it is the caller's responsibility
 * to ensure `TMutation` matches the actual mutations in the cache (e.g. by
 * specifying a `mutationKey` in `filters`).
 */
export function useMutationState<
  TResult = MutationState,
  TMutation extends Mutation<any, any, any, any> = MutationTypeFromResult<TResult>,
>(...)

This is the only approach confirmed to show the warning on hover at the call site (see issue for TypeScript Language Service API verification).

✅ Checklist

  • I have followed the steps in the Contributing guide.
  • I have tested this code locally with pnpm run test:pr.

🚀 Release Impact

  • This change affects published code, and I have generated a changeset.
  • This change is docs/CI/dev-only (no release).

Summary by CodeRabbit

  • Bug Fixes
    • Improved TypeScript typing for mutation-state selection across all query adapters so the selection callback receives more precise mutation types, improving IDE autocomplete and catching type errors earlier.
  • Chores
    • Patch version bumps applied to multiple query adapters.
  • Documentation
    • Added JSDoc note warning about caller-side narrowing when using the selection callback.

…ters

Adds a TMutation generic parameter to useMutationState (and equivalents
in all six adapters) so the select callback receives a properly-typed
Mutation instead of the base type. A MutationTypeFromResult<TResult>
helper infers TMutation automatically from TResult when TResult is a
typed MutationState, keeping the API fully backward-compatible.

Also adds a @template TMutation JSDoc block on the exported hook
function in every adapter so IDEs surface the caller-side assertion
warning on hover — the only approach confirmed to work via the
TypeScript Language Service API (inline JSDoc on type params is not
shown at call sites).

Closes TanStack#10792

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

coderabbitai Bot commented Jun 3, 2026

Review Change Stack

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 84342d76-49bc-4184-b61a-8ea81e253fca

📥 Commits

Reviewing files that changed from the base of the PR and between 509ab8c and 6f2d6ce.

📒 Files selected for processing (1)
  • packages/lit-query/src/useMutationState.ts
🚧 Files skipped from review as they are similar to previous changes (1)
  • packages/lit-query/src/useMutationState.ts

📝 Walkthrough

Walkthrough

This PR adds a TMutation generic and a MutationTypeFromResult inference helper to useMutationState across React, Vue, Solid, Svelte, Preact, and Lit adapters, retyping MutationStateOptions and useMutationState signatures so select receives a narrowed mutation type, with JSDoc documenting the caller-side assertion requirement.

Changes

useMutationState TMutation Type Narrowing

Layer / File(s) Summary
MutationTypeFromResult helper and MutationStateOptions contract
packages/react-query/src/useMutationState.ts, packages/preact-query/src/useMutationState.ts, packages/solid-query/src/useMutationState.ts, packages/svelte-query/src/types.ts, packages/vue-query/src/useMutationState.ts, packages/lit-query/src/useMutationState.ts
MutationTypeFromResult conditional type infers the appropriate Mutation type from TResult, and MutationStateOptions is updated to be generic over TResult and TMutation, allowing select to receive a narrowed mutation type instead of the base Mutation.
getResult and internal casting to apply TMutation narrowing
packages/react-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, packages/lit-query/src/useMutationState.ts
getResult helper and internal controller/state logic are updated to accept both TResult and TMutation generics, casting mutations to TMutation before applying select; runtime behavior remains unchanged.
useMutationState function signatures with TMutation and JSDoc warnings
packages/react-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, packages/lit-query/src/useMutationState.ts
Exported hook signatures introduce TMutation as a second generic (defaulting via MutationTypeFromResult<TResult>) and add JSDoc @template documentation warning that narrowing is a caller-side assertion requiring explicit filters (e.g., mutationKey) to prevent type mismatches.
Changeset entry documenting patch versions and TMutation fix
.changeset/dog-brown-fox.md
Patch version bumps for all six adapters and documents the useMutationState types enhancement adding TMutation generic with JSDoc caller-side assertion warnings.

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

Possibly related PRs

  • TanStack/query#10789: Both PRs modify packages/lit-query/src/useMutationState.ts; this PR updates select callback typing and generics while the related PR changes internal state update mechanisms to prevent redundant host updates.

Suggested reviewers

  • TkDodo

Poem

🐰 A mutation state, now typed with care,
TMutation whispers, "Narrow if you dare!"
Cast with caution, filter what you keep—
A rabbit hops on, guarding types in sleep.

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 50.00% 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 accurately summarizes the main changes: adding a TMutation generic to useMutationState with JSDoc documentation for the caller-side assertion warning.
Description check ✅ Passed The PR description comprehensively addresses the problem, solution, implementation details across all six adapters, and completes the provided template with all required sections checked.
Linked Issues check ✅ Passed The PR fully implements the requirements from issue #10792: adding TMutation generic with MutationTypeFromResult helper for type inference, surfacing the caller-side assertion warning via @template JSDoc at the call site, and applying changes across all six adapter packages.
Out of Scope Changes check ✅ Passed All changes are directly scoped to the objectives: TMutation generic additions, MutationTypeFromResult helper implementations, JSDoc @template documentation, and updates to useMutationState signatures across the six adapters with no unrelated modifications.

✏️ 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.

Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
packages/lit-query/src/useMutationState.ts (1)

177-220: ⚠️ Potential issue | 🟡 Minor | ⚡ Quick win

Merge the two adjacent JSDoc blocks so IntelliSense doesn’t shadow docs

TypeScript’s language service only uses the immediately preceding JSDoc block for documentation/typing, so the inserted @template block prevents the earlier @param/@returns/@example docs from showing on hover. Fold the @template lines into the first JSDoc block.

📝 Proposed fix (merge into the existing block)
  * `@returns` An accessor for the selected mutation state array.
+ *
+ * `@template` TResult - The type of values returned by the `select` callback.
+ * `@template` TMutation - Narrows the type of the `mutation` argument passed to
+ * `select`. This is a caller-side assertion — the mutation cache stores
+ * mutations as the base `Mutation` type, so it is the caller's responsibility
+ * to ensure `TMutation` matches the actual mutations in the cache (e.g. by
+ * specifying a `mutationKey` in `filters`).
  *
  * `@example`
  * ```ts
@@
  * }
  * ```
  */
-/**
- * `@template` TResult - The type of values returned by the `select` callback.
- * `@template` TMutation - Narrows the type of the `mutation` argument passed to
- * `select`. This is a caller-side assertion — the mutation cache stores
- * mutations as the base `Mutation` type, so it is the caller's responsibility
- * to ensure `TMutation` matches the actual mutations in the cache (e.g. by
- * specifying a `mutationKey` in `filters`).
- */
 export function useMutationState<
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@packages/lit-query/src/useMutationState.ts` around lines 177 - 220, Merge the
two adjacent JSDoc blocks so the `@template` tags are part of the main
documentation for useMutationState: move the `@template` TResult and `@template`
TMutation lines into the preceding JSDoc block that contains the
`@param/`@returns/@example so the full comment immediately precedes the export
function useMutationState declaration and IntelliSense shows the complete docs
when hovering.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Outside diff comments:
In `@packages/lit-query/src/useMutationState.ts`:
- Around line 177-220: Merge the two adjacent JSDoc blocks so the `@template` tags
are part of the main documentation for useMutationState: move the `@template`
TResult and `@template` TMutation lines into the preceding JSDoc block that
contains the `@param/`@returns/@example so the full comment immediately precedes
the export function useMutationState declaration and IntelliSense shows the
complete docs when hovering.

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 99755d61-1e5c-4566-9237-0c6fa7c42595

📥 Commits

Reviewing files that changed from the base of the PR and between b6b9d1a and 509ab8c.

📒 Files selected for processing (8)
  • .changeset/dog-brown-fox.md
  • packages/lit-query/src/useMutationState.ts
  • packages/preact-query/src/useMutationState.ts
  • packages/react-query/src/useMutationState.ts
  • packages/solid-query/src/useMutationState.ts
  • packages/svelte-query/src/types.ts
  • packages/svelte-query/src/useMutationState.svelte.ts
  • packages/vue-query/src/useMutationState.ts

…tionState

Two adjacent JSDoc blocks caused TypeScript's language service to shadow
the @param/@returns/@example docs with the @template block. Merge them
into a single block so IntelliSense shows the full documentation on hover.

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.

docs(useMutationState): TMutation safety warning is missing from hook JSDoc — only visible on type definition

1 participant