Skip to content

fix(query-core): stop wrapping persister generics in NoInfer#10510

Open
ousamabenyounes wants to merge 2 commits intoTanStack:mainfrom
ousamabenyounes:fix/issue-7842
Open

fix(query-core): stop wrapping persister generics in NoInfer#10510
ousamabenyounes wants to merge 2 commits intoTanStack:mainfrom
ousamabenyounes:fix/issue-7842

Conversation

@ousamabenyounes
Copy link
Copy Markdown

@ousamabenyounes ousamabenyounes commented Apr 16, 2026

Summary

Fixes #7842.

QueryOptions.persister was typed as QueryPersister<NoInfer<TQueryFnData>, NoInfer<TQueryKey>, NoInfer<TPageParam>>, which prevented the persister from contributing to TQueryFnData inference. When the companion queryFn declared a parameter (e.g. (_context) => 'test'), TypeScript failed to infer TQueryFnData from its return and defaulted to unknown, producing a spurious overload mismatch against a concretely-typed persister.

Removing the NoInfer wrappers lets persister participate in inference. queryFn still drives inference when the types agree, and genuine conflicts between persister and queryFn still surface as errors (covered by a new negative type test in queryOptions.test-d.tsx).

Repro

declare function createPersister<TData>(): QueryPersister<TData, any>

// Works (baseline)
queryOptions({
  queryKey: ['a'],
  queryFn: () => 'hello',
  persister: createPersister<string>(),
})

// BEFORE: TS2769 "No overload matches this call"
// AFTER: compiles cleanly
queryOptions({
  queryKey: ['b'],
  queryFn: (_context) => 'hello',
  persister: createPersister<string>(),
})

Verification

  • packages/query-core type tests: pass across TS 5.4, 5.8, 6.0 current (tsc --build tsconfig.legacy.json)
  • packages/react-query type tests: pass (new positive + negative cases in queryOptions.test-d.tsx)
  • packages/vue-query, packages/solid-query, packages/preact-query type tests: pass
  • Genuine type conflicts (e.g. queryFn: () => 42 with persister: QueryPersister<string>) still produce the expected overload mismatch — verified by the new @ts-expect-error cases.

Changeset

@tanstack/query-core: patch — type-only change, no runtime behaviour affected.

🤖 Generated with Claude Code

Summary by CodeRabbit

  • Bug Fixes

    • Fixed TypeScript type inference when using typed persisters with query functions that accept parameters, preventing spurious overload mismatch errors and improving IDE/type safety for parameterized queries.
  • Tests

    • Added type-level tests to verify correct inference from persisters and to ensure genuine type conflicts are still reported.

The `persister` field on QueryOptions was typed as
`QueryPersister<NoInfer<TQueryFnData>, NoInfer<TQueryKey>, NoInfer<TPageParam>>`
so persister could not contribute to TQueryFnData inference. When the
companion queryFn declared a parameter (e.g. `(_context) => 'test'`),
TypeScript failed to infer TQueryFnData from its return and defaulted
to `unknown`, causing a spurious overload mismatch against a
concretely-typed persister (fixes TanStack#7842).

Removing the NoInfer wrappers lets persister participate in inference.
Genuine type conflicts between persister and queryFn still surface as
errors (covered by a new negative type test in queryOptions.test-d.tsx).

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

coderabbitai bot commented Apr 16, 2026

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: bce48be1-281a-45ba-b1e6-1e9bdfd8e491

📥 Commits

Reviewing files that changed from the base of the PR and between 1546819 and cbc0246.

📒 Files selected for processing (1)
  • packages/react-query/src/__tests__/queryOptions.test-d.tsx
🚧 Files skipped from review as they are similar to previous changes (1)
  • packages/react-query/src/tests/queryOptions.test-d.tsx

📝 Walkthrough

Walkthrough

This PR removes NoInfer<> wrappers from the persister property in QueryOptions, allowing a typed persister to participate in inferring TQueryFnData even when queryFn declares parameters. Type-tests were added to validate correct and incorrect inference scenarios.

Changes

Cohort / File(s) Summary
Changeset Documentation
.changeset/fix-persister-infer-when-queryfn-has-parameter.md
Added changeset declaring a patch for @tanstack/query-core describing the persister type inference fix.
Type Definition
packages/query-core/src/types.ts
Updated QueryOptions.persister type by removing NoInfer<> wrappers: now QueryPersister<TQueryFnData, TQueryKey, TPageParam> to allow persister to participate in type inference.
Type Tests
packages/react-query/src/__tests__/queryOptions.test-d.tsx
Extended type-tests: added cases that confirm inference from persister when queryFn has parameters and negative tests verifying mismatched persister/queryFn types still error.

Estimated code review effort

🎯 2 (Simple) | ⏱️ ~10 minutes

Poem

🐰
A persister freed from NoInfer's bind,
Types now mingle, context in mind,
QueryFn calls with args at play,
Inference hops and finds its way,
Cheer the change — the types are kind!

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Title check ✅ Passed The title clearly summarizes the main change: removing NoInfer wrappers from persister generics in QueryOptions.
Description check ✅ Passed The PR description thoroughly explains the problem, solution, reproduction steps, verification, and includes a changeset declaration as required by the template.
Linked Issues check ✅ Passed All code changes directly address issue #7842 by removing NoInfer wrappers to allow persister to participate in TQueryFnData inference, eliminating the spurious TS2769 error.
Out of Scope Changes check ✅ Passed All changes are within scope: type declaration fix in QueryOptions, changeset file documenting the patch, and type tests verifying both positive and negative cases.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.

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

🧹 Nitpick comments (1)
packages/react-query/src/__tests__/queryOptions.test-d.tsx (1)

303-319: Minor: silence vitest/expect-expect for this type-only negative test.

ESLint flags this test as having no assertions because @ts-expect-error is the implicit assertion. Either disable the rule locally or wrap the calls in assertType(...) to satisfy the linter.

Proposed fix
-  it('should still error when persister and queryFn return types genuinely conflict', () => {
+  // eslint-disable-next-line vitest/expect-expect
+  it('should still error when persister and queryFn return types genuinely conflict', () => {
     const persister = undefined as unknown as QueryPersister<string, any>
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@packages/react-query/src/__tests__/queryOptions.test-d.tsx` around lines 303
- 319, This test is a type-only negative test using `@ts-expect-error` but
triggers the vitest/expect-expect lint rule; either disable the rule locally for
this block or add a no-op type assertion to make ESLint happy. Modify the test
in packages/react-query/src/__tests__/queryOptions.test-d.tsx around the two
queryOptions(...) calls: either add an inline ESLint disable comment for
"vitest/expect-expect" scoped to the test, or wrap each call with a type-level
helper such as assertType<unknown>(queryOptions(...)) (or another existing
assertType utility) so the linter sees an assertion while preserving the
`@ts-expect-error` checks on queryFn vs persister.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Nitpick comments:
In `@packages/react-query/src/__tests__/queryOptions.test-d.tsx`:
- Around line 303-319: This test is a type-only negative test using
`@ts-expect-error` but triggers the vitest/expect-expect lint rule; either disable
the rule locally for this block or add a no-op type assertion to make ESLint
happy. Modify the test in
packages/react-query/src/__tests__/queryOptions.test-d.tsx around the two
queryOptions(...) calls: either add an inline ESLint disable comment for
"vitest/expect-expect" scoped to the test, or wrap each call with a type-level
helper such as assertType<unknown>(queryOptions(...)) (or another existing
assertType utility) so the linter sees an assertion while preserving the
`@ts-expect-error` checks on queryFn vs persister.

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 878349b8-d2be-4c9a-816a-c2d3de88ede1

📥 Commits

Reviewing files that changed from the base of the PR and between dbee9eb and 1546819.

📒 Files selected for processing (3)
  • .changeset/fix-persister-infer-when-queryfn-has-parameter.md
  • packages/query-core/src/types.ts
  • packages/react-query/src/__tests__/queryOptions.test-d.tsx

Addresses CodeRabbit nitpick: vitest/expect-expect flagged the
genuine-conflict test as having no assertions. Wrap both calls in
assertType() so the linter sees an explicit assertion while the
`@ts-expect-error` directives continue to enforce the type mismatch.

Co-Authored-By: Claude <noreply@anthropic.com>
@ousamabenyounes
Copy link
Copy Markdown
Author

Addressed the vitest/expect-expect nitpick in cbc0246 — both negative cases are now wrapped in assertType(...). Types + ESLint both pass locally. Thanks for the review!

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

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Type error when using experimental_createPersister with a queryFn that has an argument

1 participant