Skip to content

chore: improve typing in devtools middleware#3362

Merged
dai-shi merged 6 commits intopmndrs:mainfrom
grigoriy-reshetniak:improve-devtools-typing
Jan 20, 2026
Merged

chore: improve typing in devtools middleware#3362
dai-shi merged 6 commits intopmndrs:mainfrom
grigoriy-reshetniak:improve-devtools-typing

Conversation

@grigoriy-reshetniak
Copy link
Copy Markdown
Contributor

Summary

While going after another FIXME comment I've noticed repetitive api as any in devtools middleware. Also fixed some as any on the way.

Check List

  • pnpm run fix for formatting and linting code and docs
  • npx tsc --noEmit to check ts errors

@vercel
Copy link
Copy Markdown

vercel Bot commented Jan 18, 2026

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Review Updated (UTC)
zustand-demo Ready Ready Preview, Comment Jan 20, 2026 1:30am

Request Review

@codesandbox-ci
Copy link
Copy Markdown

codesandbox-ci Bot commented Jan 18, 2026

This pull request is automatically built and testable in CodeSandbox.

To see build info of the built libraries, click here or the icon next to each commit SHA.

@pkg-pr-new
Copy link
Copy Markdown

pkg-pr-new Bot commented Jan 18, 2026

commit: ea4ba8d

Copy link
Copy Markdown
Member

@dai-shi dai-shi left a comment

Choose a reason for hiding this comment

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

If we were to do api as typeof api & WithDispatch, should WithDispatch have optional properties?

Comment thread src/middleware/devtools.ts Outdated
}

type WithDispatch = {
dispatch: (...args: any[]) => any
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Can we avoid any here?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Updated

Comment thread src/middleware/devtools.ts Outdated
(fn, devtoolsOptions = {}) =>
(set, get, api) => {
const { enabled, anonymousActionType, store, ...options } = devtoolsOptions
const apiWithDispatch = api as typeof api & WithDispatch
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

This increases bundle size. We hope only type improvements without affecting the result of bundling.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Removed this in favor of type guard. Now bundle size became smaller than before.

@grigoriy-reshetniak
Copy link
Copy Markdown
Contributor Author

Hello @dai-shi.
Thank you for your review!

If we were to do api as typeof api & WithDispatch, should WithDispatch have optional properties?

You right, but I already removed this line for other reason.

Copy link
Copy Markdown
Member

@dai-shi dai-shi left a comment

Choose a reason for hiding this comment

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

Nice. The type guard approach is more reasonable.

Comment thread src/middleware/devtools.ts Outdated
dispatchFromDevtools: true
}

const hasDispatch = (api: unknown): api is WithDispatch =>
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Actually, the name is confusing. This is not to check if it has the function, but if devtools should dispatch.

Suggested change
const hasDispatch = (api: unknown): api is WithDispatch =>
const shouldDispatchFromUs = (api: unknown): api is WithDispatch =>

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Done, but the name is slightly different.

Comment thread src/middleware/devtools.ts Outdated
Comment on lines +35 to +36
typeof (api as WithDispatch).dispatch === 'function' &&
(api as WithDispatch).dispatchFromDevtools
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Let's keep the original order:

Suggested change
typeof (api as WithDispatch).dispatch === 'function' &&
(api as WithDispatch).dispatchFromDevtools
(api as WithDispatch).dispatchFromDevtools &&
typeof (api as WithDispatch).dispatch === 'function'

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Done

Comment thread src/middleware/devtools.ts Outdated

type WithDispatch = {
dispatch: (...args: unknown[]) => void
dispatchFromDevtools: true
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Suggested change
dispatchFromDevtools: true
dispatchFromDevtools: unknown

should be more precise.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Done

Comment thread src/middleware/devtools.ts Outdated
}

const shouldDispatchFromDevtools = (api: unknown): api is WithDispatch =>
(api as WithDispatch).dispatchFromDevtools === true &&
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Let's not change the logic.

Suggested change
(api as WithDispatch).dispatchFromDevtools === true &&
(api as WithDispatch).dispatchFromDevtools &&

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Sorry, but this is required since dispatchFromDevtools is unknown now. The intent of shouldDispatchFromDevtools not only check if fields are existing, but that dispatchFromDevtools is true. If this is not done it won't fully substitute original behavior.

Also, leaving (api as WithDispatch).dispatchFromDevtools intact throws a TS error:

Type unknown is not assignable to type boolean

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

We didn't check if it's true, only if it's truthy. That's the original behavior. Do some workaround for the TS error. You can move it back to true from unknown if there's no workaround.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Updated. I picked !! over Boolean() because double negation already has been used in the repository.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Ah, I see. It was because of the type guard.

Copy link
Copy Markdown
Member

@dai-shi dai-shi left a comment

Choose a reason for hiding this comment

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

Copy link
Copy Markdown
Member

@dai-shi dai-shi left a comment

Choose a reason for hiding this comment

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

LGTM
Thanks for your contribution!

@dai-shi dai-shi merged commit 6e026d7 into pmndrs:main Jan 20, 2026
33 checks passed
@grigoriy-reshetniak grigoriy-reshetniak deleted the improve-devtools-typing branch January 20, 2026 04:57
@grigoriy-reshetniak
Copy link
Copy Markdown
Contributor Author

Thank you for guiding me through this.

@dai-shi dai-shi added this to the v5.0.11 milestone Feb 1, 2026
mergify Bot added a commit to robfrank/linklift that referenced this pull request Feb 14, 2026
Bumps [zustand](https://github.com/pmndrs/zustand) from 5.0.10 to 5.0.11.
Release notes

*Sourced from [zustand's releases](https://github.com/pmndrs/zustand/releases).*

> v5.0.11
> -------
>
> This release includes small improvements in middleware thanks to contributors.
>
> What's Changed
> --------------
>
> * chore: improve typing in devtools middleware by [`@​grigoriy-reshetniak`](https://github.com/grigoriy-reshetniak) in [pmndrs/zustand#3362](https://redirect.github.com/pmndrs/zustand/pull/3362)
> * fix(persist): avoid relying on global localStorage by [`@​honuuk`](https://github.com/honuuk) in [pmndrs/zustand#3367](https://redirect.github.com/pmndrs/zustand/pull/3367)
> * fix(immer): Proper typing for immer middleware in combination with slices by [`@​wheerd`](https://github.com/wheerd) in [pmndrs/zustand#3371](https://redirect.github.com/pmndrs/zustand/pull/3371)
>
> New Contributors
> ----------------
>
> * [`@​SeongYongLee`](https://github.com/SeongYongLee) made their first contribution in [pmndrs/zustand#3355](https://redirect.github.com/pmndrs/zustand/pull/3355)
> * [`@​grigoriy-reshetniak`](https://github.com/grigoriy-reshetniak) made their first contribution in [pmndrs/zustand#3351](https://redirect.github.com/pmndrs/zustand/pull/3351)
> * [`@​DormancyWang`](https://github.com/DormancyWang) made their first contribution in [pmndrs/zustand#3363](https://redirect.github.com/pmndrs/zustand/pull/3363)
> * [`@​Ea-st-ring`](https://github.com/Ea-st-ring) made their first contribution in [pmndrs/zustand#3369](https://redirect.github.com/pmndrs/zustand/pull/3369)
> * [`@​winner07`](https://github.com/winner07) made their first contribution in [pmndrs/zustand#3373](https://redirect.github.com/pmndrs/zustand/pull/3373)
> * [`@​honuuk`](https://github.com/honuuk) made their first contribution in [pmndrs/zustand#3367](https://redirect.github.com/pmndrs/zustand/pull/3367)
> * [`@​wheerd`](https://github.com/wheerd) made their first contribution in [pmndrs/zustand#3371](https://redirect.github.com/pmndrs/zustand/pull/3371)
>
> **Full Changelog**: <pmndrs/zustand@v5.0.10...v5.0.11>


Commits

* [`99379a6`](pmndrs/zustand@99379a6) 5.0.11
* [`c81b4eb`](pmndrs/zustand@c81b4eb) chore(deps): update dev dependencies ([#3375](https://redirect.github.com/pmndrs/zustand/issues/3375))
* [`3871d53`](pmndrs/zustand@3871d53) fix(immer): Proper typing for immer middleware in combination with slices (#...
* [`9b505ac`](pmndrs/zustand@9b505ac) fix(persist): use window.localStorage as default storage reference ([#3367](https://redirect.github.com/pmndrs/zustand/issues/3367))
* [`267a57c`](pmndrs/zustand@267a57c) Update code block in tutorial-tic-tac-toe.md ([#3373](https://redirect.github.com/pmndrs/zustand/issues/3373))
* [`6813f7b`](pmndrs/zustand@6813f7b) docs: remove stray Russian comment in beginner-typescript guide ([#3369](https://redirect.github.com/pmndrs/zustand/issues/3369))
* [`d9ea330`](pmndrs/zustand@d9ea330) docs(testing): fix undefined counterStoreRef variable ([#3368](https://redirect.github.com/pmndrs/zustand/issues/3368))
* [`6e026d7`](pmndrs/zustand@6e026d7) chore: improve typing in devtools middleware ([#3362](https://redirect.github.com/pmndrs/zustand/issues/3362))
* [`e7d4593`](pmndrs/zustand@e7d4593) Revert "chore(deps): bump pmndrs/docs/.github/workflows/build.yml from 2 to 3...
* [`0f49ad8`](pmndrs/zustand@0f49ad8) chore(deps): bump pmndrs/docs/.github/workflows/build.yml from 2 to 3 ([#3364](https://redirect.github.com/pmndrs/zustand/issues/3364))
* Additional commits viewable in [compare view](pmndrs/zustand@v5.0.10...v5.0.11)
  
[![Dependabot compatibility score](https://dependabot-badges.githubapp.com/badges/compatibility\_score?dependency-name=zustand&package-manager=npm\_and\_yarn&previous-version=5.0.10&new-version=5.0.11)](https://docs.github.com/en/github/managing-security-vulnerabilities/about-dependabot-security-updates#about-compatibility-scores)
Dependabot will resolve any conflicts with this PR as long as you don't alter it yourself. You can also trigger a rebase manually by commenting `@dependabot rebase`.
[//]: # (dependabot-automerge-start)
[//]: # (dependabot-automerge-end)
---
Dependabot commands and options
  
You can trigger Dependabot actions by commenting on this PR:
- `@dependabot rebase` will rebase this PR
- `@dependabot recreate` will recreate this PR, overwriting any edits that have been made to it
- `@dependabot merge` will merge this PR after your CI passes on it
- `@dependabot squash and merge` will squash and merge this PR after your CI passes on it
- `@dependabot cancel merge` will cancel a previously requested merge and block automerging
- `@dependabot reopen` will reopen this PR if it is closed
- `@dependabot close` will close this PR and stop Dependabot recreating it. You can achieve the same result by closing it manually
- `@dependabot show  ignore conditions` will show all of the ignore conditions of the specified dependency
- `@dependabot ignore this major version` will close this PR and stop Dependabot creating any more for this major version (unless you reopen the PR or upgrade to it yourself)
- `@dependabot ignore this minor version` will close this PR and stop Dependabot creating any more for this minor version (unless you reopen the PR or upgrade to it yourself)
- `@dependabot ignore this dependency` will close this PR and stop Dependabot creating any more for this dependency (unless you reopen the PR or upgrade to it yourself)
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.

2 participants