ITADN

Nested create() on draft element shares references with original base object

#160Openknutwannheden 创建于 2025-12-21
K
knutwannhedencommented
## Description When calling `create()` on an object that is already a Mutative draft proxy, the resulting object's nested properties that weren't modified in the recipe share references with the **original base object**, not the draft's current state. This allows accidental mutation of the original object, which violates immutability expectations. ## Minimal Reproduction ```typescript import { create } from 'mutative'; interface Metadata { value: string; } interface Node { name: string; metadata: Metadata; } interface Tree { nodes: Node[]; } // Helper function that uses create() internally - common pattern for reusable transformations function updateName(node: Node, newName: string): Node { return create(node, (draft) => { draft.name = newName; }); } const original: Tree = { nodes: [ { name: 'a', metadata: { value: '' } } ] }; const result = create(original, (draft) => { draft.nodes = draft.nodes.map((node) => { // Calling a helper that uses create() - doesn't look like nested create() const updated = updateName(node, 'modified'); // BUG: updated.metadata shares reference with original updated.metadata.value = 'oops'; return updated; }); }); console.log('Original mutated?', original.nodes[0].metadata.value === 'oops'); // true! ``` Note: In real code, the nested `create()` call is typically hidden inside a helper function (like `updateName` above), making it non-obvious that you're calling `create()` on a draft element. ## Expected Behavior When `create()` is called on a draft element, the resulting object should not share mutable references with the original base object. Either: 1. Nested `create()` should work correctly (like Immer handles nested `produce()`) 2. Or an error/warning should be thrown indicating this pattern isn't supported ## Actual Behavior The object returned from the inner `create()` call has nested properties (`metadata`) that reference the original base object. Mutating these nested properties unexpectedly mutates the original. ## Environment - mutative version: latest - Node.js version: v20+ ## Context We discovered this while migrating from Immer to Mutative. Our codebase uses a visitor pattern where helper functions call `create()` to modify parts of a tree, and these helpers are sometimes invoked from within a `create()` recipe. This pattern worked correctly with Immer but causes original tree mutation with Mutative.
1 条评论