Skip to content

fix(extension): accept sanitizers typed with store state and action - #2122

Open
askalf wants to merge 12 commits into
reduxjs:mainfrom
sprayberry-code:fix/sanitizer-option-types
Open

askalf wants to merge 12 commits into
reduxjs:mainfrom
sprayberry-code:fix/sanitizer-option-types

Conversation

@askalf

@askalf askalf commented Sep 20, 2026 •

Copy link
Copy Markdown

Fixes #1534

Summary

  • Correct EnhancerOptions sanitizer and predicate callback typing so concrete state/action callbacks are accepted.
  • Use method syntax to preserve the intended callback variance under strictFunctionTypes.

Decisions

The runtime already passes unknown state and Action<string> values; this changes declarations only. Method syntax is required because property syntax remains contravariant and rejects the reporter's concrete callback. The reduced case from the issue fails to compile on main (TS2769) and compiles with this change.

AI assistance: this bug was found and the fix was drafted with AI tooling in my workflow. I'm responsible for the change and will handle review feedback.

askalf and others added 8 commits September 19, 2026 01:47
`stateSanitizer`, `actionSanitizer`, and `predicate` were declared with
generics on the callback itself, so the parameter types were chosen by the
caller of the sanitizer rather than by the user writing it. Any callback
annotated with a concrete state or action type was rejected, which is the
documented way to write one.

Take `unknown` state and `Action<string>` actions, in method syntax so the
parameters stay bivariant and a narrowed annotation still assigns.

Fixes reduxjs#1534
The repo's `vitest/expect-expect` rule requires every test to assert, and a
type-only test file asserts nothing. Give each case a real expectation on the
sanitizer and on the stub the entry point returns, so the file discriminates
the fix under `tsc` and still earns its place in the vitest run.
…e tests

Adds the Config-typed options object, the logOnlyInProduction entry point,
a sanitizer annotated with the interface's own parameter types, and a
zero-argument sanitizer, plus controls for the pre-existing generic form,
falsy state values and the deliberately unchanged trace option.
The extension package's importer gains the vitest entry package.json
declares, so a frozen-lockfile install accepts it. The type tests no
longer pin trace's unrelated callback limitation with a ts-expect-error,
and the suite comment states what the options accept.
…tests

Adds the inline-annotated options object, the annotated predicate on
devToolsEnhancer and the unknown sanitized state the callers now have to
cast, each of which fails to compile against the unfixed interface, plus
a control for a predicate written with the declared parameter types. The
trace control regains the ts-expect-error that pins it as out of scope.
@changeset-bot

changeset-bot Bot commented Sep 20, 2026 •

Copy link
Copy Markdown

🦋 Changeset detected

Latest commit: 9720130

The changes in this PR will be included in the next version bump.

This PR includes changesets to release 1 package
Name Type
@redux-devtools/extension Patch

Not sure what this means? Click here to learn what changesets are.

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

askalf and others added 4 commits September 19, 2026 21:50
main bumped @types/node, jsdom and vite, so the vitest entry the
extension package's importer points at no longer existed in the merged
lockfile and pnpm install --frozen-lockfile failed with
ERR_PNPM_LOCKFILE_MISSING_DEPENDENCY. Regenerated with the pinned pnpm;
one entry changes.
The fix changes three declarations in a package that has no tests; the vitest setup and the type-test file were out of proportion to it.
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.

TypeScript error when using stateSanitizer or actionSanitizer with custom state/action types

1 participant