Shrinking an array with `length =` and growing it back loses the patches for the truncated indices

Đang mở Phù hợp với người mới
#1,355 0 bình luận 0 reaction 0 người được giao Xem trên GitHub

Maintainer thường phản hồi trong vòng 4 ngày

@RClaudiuM đang làm issue này rồi.

Từ ngày 7/11/2026.

  • #1356 của @RClaudiuM — đang mở

Đánh giá

Độ khó
2/5
Thời gian dự kiến
1-3 giờ
Mức phù hợp với người mới
75/100
Loại issue
Lỗi
Độ rõ ràng
Đặc tả rõ ràng
Mức độ hoạt động
Sôi nổi
Công nghệ
javascript
Lĩnh vực
backend

Hướng nghiên cứu

Bắt đầu trong src/plugins/patches.ts, đặc biệt là ở generateArrayPatches_, và theo dõi cách việc thu nhỏ một mảng draft làm thay đổi copy_ và assigned_. Thêm bài kiểm thử hồi quy được cung cấp cho trường hợp thu nhỏ rồi tăng lại length, sau đó chạy các bài kiểm thử liên quan đến patch. Công việc hoàn tất khi các patch xuôi tái tạo kết quả của recipe và các patch ngược khôi phục trạng thái ban đầu.

Do mô hình lập chỉ mục viết ra từ nội dung của issue.

Mô tả

🐛 Bug Report

With enablePatches(), if a recipe shrinks a draft array by assigning .length and then grows it back to at least its original length, no replace patch (and no inverse patch) is produced for the truncated indices. Replaying the patches on the base gives a different state than the recipe, and the inverse patches do not restore the base. The same recipe written with splice(0) produces correct patches.

Link to repro

Not applicable: no CodeSandbox account; the script below and the unit test at the end reproduce it.

To Reproduce

npm i [email protected], save as t.mjs, run node t.mjs:

import { produceWithPatches, applyPatches, enablePatches } from "immer";
enablePatches();

const base = [1];
const [result, patches, inverse] = produceWithPatches(base, d => {
  d.length = 0; // truncate index 0
  d[1] = 2;     // grow again
});

console.log(result);
console.log(patches);
console.log(inverse);
console.log(applyPatches(base, patches));
console.log(applyPatches(result, inverse));

const [r2, p2, i2] = produceWithPatches([1, 2], d => { d.length = 0; d.length = 2; });
console.log(r2, p2, i2);

Observed behavior

[ <1 empty item>, 2 ]
[ { op: 'add', path: [ 1 ], value: 2 } ]
[ { op: 'remove', path: [ 1 ] } ]
[ 1, 2 ]
[ <1 empty item> ]
[ <2 empty items> ] [] []

applyPatches(base, patches) keeps the stale 1 at index 0, and applyPatches(result, inverse) does not bring it back. The second recipe changes both elements but produces no patches at all.

Expected behavior

The same patches as for d.splice(0); d[1] = 2, which immer already gets right:

[
  { op: 'replace', path: [ 0 ], value: undefined },
  { op: 'add', path: [ 1 ], value: 2 }
] [
  { op: 'replace', path: [ 0 ], value: 1 },
  { op: 'remove', path: [ 1 ] }
]

so that applyPatches(base, patches) gives [ undefined, 2 ] and applyPatches(result, inverse) gives [ 1 ].

generateArrayPatches_ in src/plugins/patches.ts only emits a replace for an index below base.length when assigned_.get(i) is set (or allIndicesReassigned_). Assigning a smaller length truncates copy_ without marking the dropped indices as assigned, so they are skipped once the array is long again.

Unit test:

test("length shrink then grow produces replace patches", () => {
  const base = [1]
  const [result, patches, inverse] = produceWithPatches(base, d => {
    d.length = 0
    d[1] = 2
  })
  expect(applyPatches(base, patches)).toEqual(result)
  expect(applyPatches(result, inverse)).toEqual(base)
})

Environment

We only accept bug reports against the latest Immer version.

  • Immer version: 11.2.9 (also main 75a290e, built from source), Node.js v26.13.0
  • I filed this report against the latest version of Immer
  • Occurs with setUseProxies(true)
  • Occurs with setUseProxies(false) (ES5 only)

Credit: https://ucsbplse.github.io/

Ngôn ngữ chính
JavaScript
Star
29k
Fork
905
Merge trung bình
3 ngày 16 giờ
Pull request đã merge (30 ngày)
22

Chuẩn bị môi trường

Dự án này không cung cấp dev container, Dockerfile hay hướng dẫn đóng góp, nên bạn cần tự thiết lập môi trường: hãy bắt đầu từ README và xem hướng dẫn đóng góp lần đầu của chúng tôi để biết các bước chung.

Bắt đầu từ đâu

  1. Đọc hết issue, rồi đọc hướng dẫn đóng góp của dự án.
  2. Bình luận trên issue rằng bạn sẽ nhận — tránh hai người làm cùng một việc.
  3. Fork repository và làm thay đổi trên một nhánh.
  4. Mở pull request có tham chiếu số hiệu của issue.

Issue khác của immerjs/immer

Tất cả issue của immerjs/immer

Issue tương tự

Thêm issue về JavaScript

Nhận issue mới trong hộp thư của bạn

Bản tóm tắt ngắn những issue GitHub phù hợp với người mới.