Skip to content

fix: preserve Map state when reassigning NaN - #1324

Open
maxazure wants to merge 2 commits into
immerjs:mainfrom
maxazure:fix/map-set-nan
Open

maxazure wants to merge 2 commits into
immerjs:mainfrom
maxazure:fix/map-set-nan

Conversation

@maxazure

Copy link
Copy Markdown

Setting an existing Map entry from NaN to NaN currently creates a new Map and, when nested, a new parent object even though the value is unchanged.

Treat two NaN values as equal in the Map.set change check. Keep the existing +0/-0 behavior and missing-key handling.

Added seven cases across the existing nine configurations, including both auto-freeze modes. Before the fix, 18 cases failed and 45 passed; afterward, all 63 passed. The tests cover root and nested structural sharing, NaN ↔ number changes, missing keys, and both signed-zero directions.

Validation on Node 25.2.1 / Yarn 1.22.22:

  • yarn test: 3,899 source tests and 3,348 production-build tests passed, with eight existing skips in each suite; build and Flow passed.
  • yarn coverage, yarn test:perf, and git diff --check passed.
  • Source type checking with --skipLibCheck --moduleResolution node passed. Default tsc --noEmit has the same dependency-declaration errors on the unmodified baseline. Whole-file Prettier differences are also identical to baseline; the added code is formatted.

Prepared with AI assistance, including code, tests, and a separate AI review of the final diff. No human review is claimed.

Copilot AI balanced review requested due to automatic review settings October 13, 2026 01:50

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🟢 Approval recommended

The five-line fix correctly implements SameValueZero equality matching the existing proxy.ts NaN pattern, and is fully covered by correct, comprehensive new tests.

0 open findings

What changed in this PR

This PR fixes a structural-sharing bug in Immer's Map support: setting an existing Map entry from NaN to NaN previously produced a new Map (and, when nested, a new parent object) because the change check relied on !==, and NaN !== NaN is always true. The fix adds a NaN-equality guard to the Map.set change-detection condition so reassigning NaN to NaN is treated as a no-op, while preserving existing +0/-0 and missing-key handling.

Changes:

  • In mapset.ts, extract current and treat two NaN values as equal in the set change check (current !== value && !(Number.isNaN(current) && Number.isNaN(value))), giving SameValueZero semantics consistent with the pattern already used in proxy.ts:208.
  • Add seven test cases covering root/nested NaN→NaN sharing, NaN↔number changes, missing-key insertion, and both signed-zero directions.
File Description
src/​plugins/​mapset.ts Adds a NaN-aware guard to the Map.set change check so identical NaN reassignment no longer marks the Map as changed.
__tests__/​base.js Adds tests for NaN→NaN sharing (root and nested), NaN↔number changes, missing-key NaN insertion, and +0/-0 preservation.

🧠 Review effort: Balanced


💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.

This branch has not been deployed

No deployments
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