Skip to content

refactor: narrow onSuccess/onError/onMutate/onSettled callback types to Promise<void> | void #9202

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 2 commits into from
Jun 2, 2025

Conversation

braden-w
Copy link
Contributor

Previously, the onSuccess, onError, onMutate, and onSettled callbacks were typed as () => Promise<unknown> | unknown. While unknown is technically valid, it implies that the return value might be used, assigned, or further processed. However, throughout the codebase, these callbacks are invoked solely for their side effects, and their return values are always ignored. Narrowing the type to Promise<void> | void makes this intent explicit, clarifies that any return value will be discarded, and prevents misleading type signatures that suggest otherwise.

This commit narrows their types to () => Promise<void> | void, which more accurately reflects their intended use.

Copy link

nx-cloud bot commented May 28, 2025

View your CI Pipeline Execution ↗ for commit 26a2647.

Command Status Duration Result
nx affected --targets=test:sherif,test:knip,tes... ✅ Succeeded 1m 7s View ↗
nx run-many --target=build --exclude=examples/*... ✅ Succeeded 1s View ↗

☁️ Nx Cloud last updated this comment at 2025-05-28 07:08:18 UTC

Copy link

pkg-pr-new bot commented May 28, 2025

More templates

@tanstack/angular-query-devtools-experimental

npm i https://pkg.pr.new/@tanstack/angular-query-devtools-experimental@9202

@tanstack/angular-query-experimental

npm i https://pkg.pr.new/@tanstack/angular-query-experimental@9202

@tanstack/eslint-plugin-query

npm i https://pkg.pr.new/@tanstack/eslint-plugin-query@9202

@tanstack/query-async-storage-persister

npm i https://pkg.pr.new/@tanstack/query-async-storage-persister@9202

@tanstack/query-broadcast-client-experimental

npm i https://pkg.pr.new/@tanstack/query-broadcast-client-experimental@9202

@tanstack/query-core

npm i https://pkg.pr.new/@tanstack/query-core@9202

@tanstack/query-devtools

npm i https://pkg.pr.new/@tanstack/query-devtools@9202

@tanstack/query-persist-client-core

npm i https://pkg.pr.new/@tanstack/query-persist-client-core@9202

@tanstack/query-sync-storage-persister

npm i https://pkg.pr.new/@tanstack/query-sync-storage-persister@9202

@tanstack/react-query

npm i https://pkg.pr.new/@tanstack/react-query@9202

@tanstack/react-query-devtools

npm i https://pkg.pr.new/@tanstack/react-query-devtools@9202

@tanstack/react-query-next-experimental

npm i https://pkg.pr.new/@tanstack/react-query-next-experimental@9202

@tanstack/react-query-persist-client

npm i https://pkg.pr.new/@tanstack/react-query-persist-client@9202

@tanstack/solid-query

npm i https://pkg.pr.new/@tanstack/solid-query@9202

@tanstack/solid-query-devtools

npm i https://pkg.pr.new/@tanstack/solid-query-devtools@9202

@tanstack/solid-query-persist-client

npm i https://pkg.pr.new/@tanstack/solid-query-persist-client@9202

@tanstack/svelte-query

npm i https://pkg.pr.new/@tanstack/svelte-query@9202

@tanstack/svelte-query-devtools

npm i https://pkg.pr.new/@tanstack/svelte-query-devtools@9202

@tanstack/svelte-query-persist-client

npm i https://pkg.pr.new/@tanstack/svelte-query-persist-client@9202

@tanstack/vue-query

npm i https://pkg.pr.new/@tanstack/vue-query@9202

@tanstack/vue-query-devtools

npm i https://pkg.pr.new/@tanstack/vue-query-devtools@9202

commit: 26a2647

Copy link

codecov bot commented May 28, 2025

Codecov Report

All modified and coverable lines are covered by tests ✅

Project coverage is 59.56%. Comparing base (a1210cb) to head (26a2647).
Report is 6 commits behind head on main.

Additional details and impacted files

Impacted file tree graph

@@             Coverage Diff             @@
##             main    #9202       +/-   ##
===========================================
+ Coverage   45.24%   59.56%   +14.32%     
===========================================
  Files         209      138       -71     
  Lines        8247     5485     -2762     
  Branches     1860     1467      -393     
===========================================
- Hits         3731     3267      -464     
+ Misses       4076     1921     -2155     
+ Partials      440      297      -143     
Components Coverage Δ
@tanstack/angular-query-devtools-experimental ∅ <ø> (∅)
@tanstack/angular-query-experimental 85.04% <ø> (ø)
@tanstack/eslint-plugin-query ∅ <ø> (∅)
@tanstack/query-async-storage-persister 43.85% <ø> (ø)
@tanstack/query-broadcast-client-experimental 24.39% <ø> (ø)
@tanstack/query-codemods ∅ <ø> (∅)
@tanstack/query-core 98.12% <ø> (ø)
@tanstack/query-devtools 3.56% <ø> (ø)
@tanstack/query-persist-client-core 78.32% <ø> (ø)
@tanstack/query-sync-storage-persister 84.61% <ø> (ø)
@tanstack/query-test-utils ∅ <ø> (∅)
@tanstack/react-query 96.00% <ø> (ø)
@tanstack/react-query-devtools 10.00% <ø> (ø)
@tanstack/react-query-next-experimental ∅ <ø> (∅)
@tanstack/react-query-persist-client 100.00% <ø> (ø)
@tanstack/solid-query 78.20% <ø> (ø)
@tanstack/solid-query-devtools ∅ <ø> (∅)
@tanstack/solid-query-persist-client 100.00% <ø> (ø)
@tanstack/svelte-query 88.15% <ø> (ø)
@tanstack/svelte-query-devtools ∅ <ø> (∅)
@tanstack/svelte-query-persist-client 100.00% <ø> (ø)
@tanstack/vue-query 70.85% <ø> (ø)
@tanstack/vue-query-devtools ∅ <ø> (∅)
🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

@github-actions github-actions bot added the documentation Improvements or additions to documentation label May 28, 2025
braden-w added a commit to epicenterhq/query that referenced this pull request May 28, 2025
This commit builds on the changes introduced in [refactor/narrow-callback-types](TanStack#9202) and replaces all instances of `Promise<T> | T` with the new `MaybePromise<T>` helper type. The `MaybePromise` type, originally defined in `@tanstack/query-persist-client-core/src/createPersister.ts`, is now moved to `query-core` for broader, consistent use.

- Moves the `MaybePromise` type definition to `query-core`, making it the canonical utility for representing values that may be returned synchronously or as a promise.
- Updates all relevant callback signatures (such as `onSuccess`, `onError`, `onMutate`, `onSettled`, and other callbacks) to use `MaybePromise<T>` instead of `Promise<T> | T`.
- Updates documentation to reference `MaybePromise<T>` for clarity and consistency.

This builds on the direction set by [refactor/narrow-callback-types](TanStack#9202), further improving type readability and maintainability by using a single, expressive type for all maybe-async callback returns.
braden-w added 2 commits May 28, 2025 00:03
…to Promise<void> | void

Previously, the `onSuccess`, `onError`, `onMutate`, and `onSettled` callbacks were typed as `() => Promise<unknown> | unknown`. While `unknown` is technically valid, it implies that the return value might be used, assigned, or further processed. However, throughout the codebase, these callbacks are invoked solely for their side effects, and their return values are always ignored. Narrowing the type to `Promise<void> | void` makes this intent explicit, clarifies that any return value will be discarded, and prevents misleading type signatures that suggest otherwise.

This commit narrows their types to `() => Promise<void> | void`, which more accurately reflects their intended use.
…n-related documentation

This commit refines the documentation for mutation-related callbacks (`onSuccess`, `onError`, `onSettled`, and `onMutate`), changing their return types from `Promise<unknown> | unknown` to `Promise<void> | void`.
@braden-w braden-w force-pushed the refactor/narrow-callback-types branch from 52de5b7 to 26a2647 Compare May 28, 2025 07:05
braden-w added a commit to epicenterhq/query that referenced this pull request May 28, 2025
This commit builds on the changes introduced in [refactor/narrow-callback-types](TanStack#9202) and replaces all instances of `Promise<T> | T` with the new `MaybePromise<T>` helper type. The `MaybePromise` type, originally defined in `@tanstack/query-persist-client-core/src/createPersister.ts`, is now moved to `query-core` for broader, consistent use.

- Moves the `MaybePromise` type definition to `query-core`, making it the canonical utility for representing values that may be returned synchronously or as a promise.
- Updates all relevant callback signatures (such as `onSuccess`, `onError`, `onMutate`, `onSettled`, and other callbacks) to use `MaybePromise<T>` instead of `Promise<T> | T`.
- Updates documentation to reference `MaybePromise<T>` for clarity and consistency.

This builds on the direction set by [refactor/narrow-callback-types](TanStack#9202), further improving type readability and maintainability by using a single, expressive type for all maybe-async callback returns.
@braden-w braden-w changed the title refactor: narrow onSuccess/onError/onMutate/onSettled callback types to Promise<void> | void in persist-query-client refactor: narrow onSuccess/onError/onMutate/onSettled callback types to Promise<void> | void May 29, 2025
@TkDodo TkDodo merged commit 982f6ca into TanStack:main Jun 2, 2025
7 checks passed
@Sagie501
Copy link

Sagie501 commented Jun 3, 2025

@braden-w @TkDodo
I have the following code:

export const useCreateProduct = () => {
  const api = useApi();
  const queryClient = useQueryClient();

  return useMutation({
    mutationFn: (createProductDto: CreateProductDto) =>
      api.products.productControllerCreateProduct(createProductDto).then((res) => res.data),
    onSuccess: (data) =>
      Promise.all([queryClient.invalidateQueries({ queryKey: /* some query key */ }), queryClient.invalidateQueries({ queryKey: /* some other query key */ })]),
  });
};

And with the following change I am getting the following error:
image

What would you suggest me to do?

@jgarplind
Copy link

jgarplind commented Jun 3, 2025

Same issue as @Sagie501 for us. Not trying to start a flood of +1s, but I think it makes sense to clarify that this is a somewhat commonly used pattern, probably impacting many users.

@Sagie501
Copy link

Sagie501 commented Jun 3, 2025

Changing the callbacks type to void | Promise<void> | Promise<Array<void>> will solve my case, does that making sense?

@braden-w
Copy link
Contributor Author

braden-w commented Jun 3, 2025

@TkDodo just made a quick PR #9245 to address the points from @Sagie501 @jgarplind —let me know what you think!

@braden-w
Copy link
Contributor Author

braden-w commented Jun 3, 2025

@Sagie501 @jgarplind For now, you can fix this by using the void prefix:

mutationFn: (createProductDto: CreateProductDto) =>
  void api.products.productControllerCreateProduct(createProductDto).then((res) => res.data),

or using braces:

mutationFn: (createProductDto: CreateProductDto) => {
  api.products.productControllerCreateProduct(createProductDto).then((res) => res.data)
},

In the first method with void, we're telling Typescript to evaluate api.products.productControllerCreateProduct(createProductDto).then((res) => res.data) but then return undefined (in other words, discard the returned result).

In the second method, we are using braces without any explicit return, so the function implicitly returns undefined.

Both methods would satisfy the current typing.

In the meantime, we can see if #9241 #9245 is a good candidate for merging.

@braden-w
Copy link
Contributor Author

braden-w commented Jun 3, 2025

@Sagie501 I actually ended up changing the return types to just void in #9245! This would end up matching the widest variety of return types from a function, while still implying that we're discarding the return value. void | Promise<void> was being problematic. We're discussing changing the return type in #9245 !

braden-w added a commit to epicenterhq/query that referenced this pull request Jun 5, 2025
…callback types to Promise<void> | void (TanStack#9202)"

This reverts commit 982f6ca.
braden-w added a commit to epicenterhq/query that referenced this pull request Jun 5, 2025
Adds test coverage for mutation callback return types, inspired by the discussions and [TkDodo's comment](TanStack#9245 (comment)
) in TanStack#9245 and the original Promise.all() edge case identified in TanStack#9202.

The original PR TanStack#9202 narrowed callback return types from `Promise<unknown> | unknown`
to `Promise<void> | void`, but this broke common patterns like:

```ts
onSuccess: (data) => Promise.all([
  invalidateQueries(),
  trackAnalytics(),
])
```

As noted in [the original discussion](TanStack#9202 (comment)),
this `Promise.all()` pattern was a legitimate use case that many users relied on.

These tests ensure we support all the callback patterns that users expect:

✅ **Sync patterns**: Implicit void, explicit void, non-void returns
✅ **Async patterns**: Async functions, Promise.resolve(), Promise<T> returns
✅ **Promise.all() patterns**: The original breaking case from TanStack#9202
✅ **Promise.allSettled() patterns**: Additional parallel operation support
✅ **Mixed patterns**: Different callback types in same mutation
✅ **Error handling**: All patterns work in error scenarios
✅ **Return value isolation**: Callback returns don't affect mutation result
TkDodo pushed a commit that referenced this pull request Jun 5, 2025
…allback types to Promise<void> | void (#9202)" (#9251)

This reverts commit 982f6ca.
TkDodo added a commit that referenced this pull request Jun 5, 2025
* Revert "refactor(types): narrow onSuccess/onError/onMutate/onSettled callback types to Promise<void> | void (#9202)"

This reverts commit 982f6ca.

* test: add callback return type tests for mutation callbacks

Adds test coverage for mutation callback return types, inspired by the discussions and [TkDodo's comment](#9245 (comment)
) in #9245 and the original Promise.all() edge case identified in #9202.

The original PR #9202 narrowed callback return types from `Promise<unknown> | unknown`
to `Promise<void> | void`, but this broke common patterns like:

```ts
onSuccess: (data) => Promise.all([
  invalidateQueries(),
  trackAnalytics(),
])
```

As noted in [the original discussion](#9202 (comment)),
this `Promise.all()` pattern was a legitimate use case that many users relied on.

These tests ensure we support all the callback patterns that users expect:

✅ **Sync patterns**: Implicit void, explicit void, non-void returns
✅ **Async patterns**: Async functions, Promise.resolve(), Promise<T> returns
✅ **Promise.all() patterns**: The original breaking case from #9202
✅ **Promise.allSettled() patterns**: Additional parallel operation support
✅ **Mixed patterns**: Different callback types in same mutation
✅ **Error handling**: All patterns work in error scenarios
✅ **Return value isolation**: Callback returns don't affect mutation result

* refactor(tests): remove unnecessary parameters in mutation tests

* test(mutations): fix mutation test ordering with error handling and cleanup verification

---------

Co-authored-by: Dominik Dorfmeister <office@dorfmeister.cc>
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.

4 participants