Skip to content

Conversation

@shkumbinhasani
Copy link

@shkumbinhasani shkumbinhasani commented Jan 13, 2026

Summary

  • infer hook error types from Throws return annotations when provided
  • guard any and strip Throws to avoid pollution in data inference
  • add type tests for auto inference and Promise<any> fallback

Testing

  • pnpm --filter @tanstack/react-query test:types (fails in test:types:tscurrent at packages/react-query/src/__tests__/useQuery.test.tsx:2186: NotifyOnChangeProps type mismatch)

Summary by CodeRabbit

Release Notes

  • New Features
    • Enhanced TypeScript error type inference in query and mutation operations for improved IDE autocomplete and compile-time type checking.
    • New type utilities for declaring and extracting error types from functions, enabling more precise error handling and type safety.

✏️ Tip: You can customize this high-level summary in your review settings.

@changeset-bot
Copy link

changeset-bot bot commented Jan 13, 2026

⚠️ No Changeset found

Latest commit: d1365c2

Merging this PR will not cause a version bump for any packages. If these changes should not result in a new version, you're good to go. If these changes should result in a version bump, you need to add a changeset.

This PR includes no changesets

When changesets are added to this PR, you'll see the packages that this PR includes changesets for and the associated semver types

Click here to learn what changesets are, and how to add one.

Click here if you're a maintainer who wants to add a changeset to this PR

@coderabbitai
Copy link
Contributor

coderabbitai bot commented Jan 13, 2026

📝 Walkthrough

Walkthrough

Introduces a comprehensive TypeScript type system for expressing and extracting error types from function return types using phantom types (Throws<TError>) and utility types (ExtractThrows, InferErrorFromFn, etc.), along with new overloads for useMutation and useQuery hooks that leverage these types for improved error type inference.

Changes

Cohort / File(s) Summary
Type utilities
packages/query-core/src/types.ts
Added 9 type-level utilities: throwsSymbol constant, Throws<TError> phantom type, ExtractThrows<T>, ExtractThrowsFromReturnType<T>, InferErrorFromFn<TFn, TFallback>, StripThrows<T>, IsAny<T>, HasThrows<TFn>, and ThrowsFnOptions<TFn, TOptions>. These enable expressing and extracting error types at the type level.
Hook overloads
packages/react-query/src/useMutation.ts, packages/react-query/src/useQuery.ts
Added new public overloads to useMutation and useQuery that accept ThrowsFnOptions and leverage InferErrorFromFn to automatically extract error types from mutation/query functions marked with Throws<E>. Imported supporting types from query-core.
Type tests
packages/react-query/src/__tests__/mutationOptions.test-d.tsx, packages/react-query/src/__tests__/useQuery.test-d.tsx
Added type-level tests validating the new Throws pattern for error typing, including explicit generics, type inference from Throws, extraction via InferErrorFromFn, and default error handling. Imported Throws and InferErrorFromFn for test definitions.

Suggested labels

package: query-core, package: react-query

Suggested reviewers

  • TkDodo
  • arnoud-dv

Poem

🐰 With phantom types and throws so bright,
Error inference typed just right,
useMutation and useQuery now see,
What errors their functions shall be! ✨

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~45 minutes

🚥 Pre-merge checks | ✅ 1 | ❌ 2
❌ Failed checks (1 warning, 1 inconclusive)
Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
Title check ❓ Inconclusive The title uses 'WIP' (work-in-progress) prefix which signals incomplete work, and is vague about the specific benefits of inferring errors from Throws returns. Remove the 'WIP' prefix and clarify the main benefit; consider: 'Add error type inference from Throws annotations in hooks' or similar.
✅ Passed checks (1 passed)
Check name Status Explanation
Description check ✅ Passed The pull request description includes a clear summary of changes, identifies testing approach, and acknowledges a known issue with type compatibility.

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

✨ Finishing touches
  • 📝 Generate docstrings

📜 Recent review details

Configuration used: defaults

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between bf7f47e and d1365c2.

📒 Files selected for processing (5)
  • packages/query-core/src/types.ts
  • packages/react-query/src/__tests__/mutationOptions.test-d.tsx
  • packages/react-query/src/__tests__/useQuery.test-d.tsx
  • packages/react-query/src/useMutation.ts
  • packages/react-query/src/useQuery.ts
🧰 Additional context used
🧠 Learnings (4)
📓 Common learnings
Learnt from: sukvvon
Repo: TanStack/query PR: 9892
File: packages/solid-query-persist-client/src/__tests__/PersistQueryClientProvider.test.tsx:331-335
Timestamp: 2025-11-22T09:06:05.219Z
Learning: In TanStack/query test files, when a queryFn contains side effects (e.g., setting flags for test verification), prefer async/await syntax for clarity; when there are no side effects, prefer the .then() pattern for conciseness.
📚 Learning: 2025-08-19T03:18:18.303Z
Learnt from: oscartbeaumont
Repo: TanStack/query PR: 9564
File: packages/solid-query-devtools/src/production.tsx:2-3
Timestamp: 2025-08-19T03:18:18.303Z
Learning: In the solid-query-devtools package, the codebase uses a pattern of type-only default imports combined with typeof for component type annotations (e.g., `import type SolidQueryDevtoolsComp from './devtools'` followed by `typeof SolidQueryDevtoolsComp`). This pattern is consistently used across index.tsx and production.tsx files, and the maintainers prefer consistency over changing this approach.

Applied to files:

  • packages/query-core/src/types.ts
  • packages/react-query/src/__tests__/useQuery.test-d.tsx
📚 Learning: 2025-11-02T22:52:33.071Z
Learnt from: DogPawHat
Repo: TanStack/query PR: 9835
File: packages/query-core/src/__tests__/queryClient.test-d.tsx:242-256
Timestamp: 2025-11-02T22:52:33.071Z
Learning: In the TanStack Query codebase, the new `query` and `infiniteQuery` methods support the `select` option for data transformation, while the legacy `fetchQuery` and `fetchInfiniteQuery` methods do not support `select` and should reject it at the type level.

Applied to files:

  • packages/react-query/src/useQuery.ts
  • packages/react-query/src/__tests__/mutationOptions.test-d.tsx
📚 Learning: 2025-11-22T09:06:05.219Z
Learnt from: sukvvon
Repo: TanStack/query PR: 9892
File: packages/solid-query-persist-client/src/__tests__/PersistQueryClientProvider.test.tsx:331-335
Timestamp: 2025-11-22T09:06:05.219Z
Learning: In TanStack/query test files, when a queryFn contains side effects (e.g., setting flags for test verification), prefer async/await syntax for clarity; when there are no side effects, prefer the .then() pattern for conciseness.

Applied to files:

  • packages/react-query/src/useQuery.ts
  • packages/react-query/src/__tests__/mutationOptions.test-d.tsx
  • packages/react-query/src/useMutation.ts
  • packages/react-query/src/__tests__/useQuery.test-d.tsx
🧬 Code graph analysis (4)
packages/react-query/src/useQuery.ts (2)
packages/query-core/src/types.ts (6)
  • QueryKey (53-61)
  • QueryFunction (182-186)
  • StripThrows (155-156)
  • ThrowsFnOptions (179-180)
  • InferErrorFromFn (145-148)
  • NoInfer (37-37)
packages/react-query/src/queryOptions.ts (2)
  • DefinedInitialDataOptions (40-50)
  • UndefinedInitialDataOptions (13-23)
packages/react-query/src/__tests__/mutationOptions.test-d.tsx (1)
packages/query-core/src/types.ts (2)
  • Throws (119-119)
  • InferErrorFromFn (145-148)
packages/react-query/src/useMutation.ts (3)
packages/query-core/src/types.ts (4)
  • StripThrows (155-156)
  • MutationFunction (1190-1193)
  • InferErrorFromFn (145-148)
  • DefaultError (47-51)
packages/query-core/src/queryClient.ts (1)
  • QueryClient (61-648)
packages/query-core/src/index.ts (1)
  • QueryClient (19-19)
packages/react-query/src/__tests__/useQuery.test-d.tsx (2)
packages/react-query/src/useQuery.ts (1)
  • useQuery (102-104)
packages/query-core/src/types.ts (2)
  • Throws (119-119)
  • InferErrorFromFn (145-148)
🔇 Additional comments (13)
packages/query-core/src/types.ts (2)

155-156: StripThrows may not fully remove the phantom type from intersection types.

When T is User & Throws<ApiError>, the condition T extends Throws<any> is true, so Omit<T, throwsSymbol> is returned. However, Omit only removes the throwsSymbol key, which should work correctly for the intersection case.

Actually, upon closer inspection, this works correctly because User & Throws<ApiError> does extend Throws<any>, and omitting the symbol key strips the phantom marker. The implementation is sound.


96-181: Well-designed phantom type system for error inference.

The type utilities correctly:

  • Guard against any to prevent pollution of error inference
  • Extract error types from both sync and async function return types
  • Provide appropriate fallbacks when Throws<E> is not present
  • Use conditional types to enable overload selection via ThrowsFnOptions

The IsAny trick (0 extends 1 & T) is a well-known TypeScript pattern for detecting any.

packages/react-query/src/__tests__/mutationOptions.test-d.tsx (2)

5-12: Imports correctly include new type utilities.

The imports of InferErrorFromFn and Throws from @tanstack/query-core align with the new type utilities added to the core types.


220-282: Comprehensive type tests for the Throws pattern.

The test suite covers the key scenarios:

  1. Explicit error type via generics
  2. Inferred error type from Throws return annotation
  3. Using InferErrorFromFn helper for extraction
  4. Default Error fallback when Throws is not used
packages/react-query/src/useQuery.ts (3)

48-70: New overload for UndefinedInitialDataOptions with Throws inference.

This overload follows the same pattern for queries without defined initial data. The implementation is consistent with the DefinedInitialDataOptions overload.


24-46: The overload correctly preserves initialData as required.

When Omit<DefinedInitialDataOptions<...>, 'queryFn'> is applied, only the queryFn property is removed—all other properties retain their original required/optional status. Since initialData is defined as required in DefinedInitialDataOptions, it remains required in the overload. The & { queryFn: TQueryFn } intersection then makes queryFn explicitly required, completing the type correctly.

Likely an incorrect or invalid review comment.


24-70: The specific test failure reference appears incorrect.

The review mentions a type test failure at useQuery.test.tsx:2186, but that line contains a functional test—not a type test. Type tests run on .test-d.tsx files via test:types:tscurrent. The useQuery.test-d.tsx file (443 lines) contains no type assertions related to notifyOnChangeProps or type mismatches. Additionally, NotifyOnChangeProps (capitalized) does not appear in the react-query source; it's defined only in query-core and refers to InfiniteQueryObserverResult keys.

The new overloads (lines 24–70) focus on the ThrowsFnOptions pattern for inferring error types from query functions. While the concern about overload ordering is valid in principle, there is no evidence of type inference issues in the current implementation.

Likely an incorrect or invalid review comment.

packages/react-query/src/__tests__/useQuery.test-d.tsx (3)

5-11: Updated imports for Throws pattern types.

The imports correctly include InferErrorFromFn and Throws from the parent module for use in type tests.


111-115: Good test for Promise<any> edge case.

This test verifies that when queryFn returns Promise<any>, the error type correctly falls back to Error rather than being polluted by any. This is critical for the IsAny guard in the type utilities.


349-442: Comprehensive type tests for Throws pattern with useQuery.

The test suite covers:

  1. Explicit error type via generics
  2. Inferred error type from Throws return annotation
  3. InferErrorFromFn helper for type extraction
  4. DefaultError fallback for functions without Throws
  5. Union error types (NetworkError | ValidationError)

This provides good coverage of the feature's type inference capabilities.

packages/react-query/src/useMutation.ts (3)

24-28: Helper types for mutation function inference.

MutationFnData and MutationFnVariables are well-designed:

  • MutationFnData correctly strips the Throws phantom from the awaited return type
  • MutationFnVariables extracts the first parameter or defaults to void for parameterless functions

27-28: Consider edge case: mutation functions with multiple parameters.

MutationFnVariables only extracts the first parameter. Per the MutationFunction type definition (line 1189-1192 in types.ts), mutation functions receive (variables, context). This is correct because variables is always the first parameter.

However, if someone passes a function with no parameters (e.g., () => Promise<Data>), Parameters<TMutationFn> would be [], and Parameters<TMutationFn>[0] would be undefined. The ternary correctly handles this by returning void when Parameters<TMutationFn> extends [].


32-54: New overload for mutation with Throws inference.

The overload correctly:

  • Uses ThrowsFnOptions to only match when mutationFn has a Throws<E> annotation
  • Infers data, error, and variables types from the mutation function
  • Preserves TOnMutateResult as a separate generic parameter

The pattern is consistent with the useQuery overloads.


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.

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.

1 participant