Skip to content

Conversation

@QDyanbing
Copy link
Contributor

@QDyanbing QDyanbing commented Dec 22, 2025

问题点:util 的 mergeWith 内部用 Reflect.ownKeys 遍历键,数组场景下会得到 length。合并多个 source 时,后一个稀疏/更短的数组会把前面已合并好的长度覆盖掉,导致数组被截断、元素丢失。

现处理:在 mergeWith 的数组分支里遍历键时跳过 length,只递归真实索引键,保持数组整体覆盖语义但不改写已有长度。这样不会再被后续数组的 length 破坏结果。

fix ant-design/ant-design#56245

Summary by CodeRabbit

  • Bug Fixes

    • 修复合并逻辑,合并时会忽略不可枚举属性,避免将非枚举项或数组长度作为递归目标,提升深层对象/数组合并的准确性与稳定性。
  • Tests

    • 新增针对稀疏数组更新与数组合并场景的测试,验证合并结果与数组长度符合预期。

✏️ Tip: You can customize this high-level summary in your review settings.

@vercel
Copy link

vercel bot commented Dec 22, 2025

@QDyanbing is attempting to deploy a commit to the React Component Team on Vercel.

A member of the Team first needs to authorize it.

@coderabbitai
Copy link

coderabbitai bot commented Dec 22, 2025

Note

Other AI code review bot(s) detected

CodeRabbit has detected other AI code review bot(s) in this pull request and will avoid duplicating their findings in the review comments. This may lead to a less comprehensive review.

Walkthrough

新增针对 mergeWith 的单元测试,并修改 src/utils/set.ts 的合并遍历逻辑:在对对象键进行递归合并前,检查该属性是否为可枚举(enumerable),仅对可枚举属性执行递归合并。

Changes

变化组 / 文件(s) 变化摘要
新增测试
src/test/mergeWith.test.ts
添加针对 mergeWith 的测试:构造全量值与稀疏更新、使用 prepareArray 保留当前数组内容,断言合并结果为 ['A', 'BB', 'C', 'D'] 且长度为 4。
合并遍历行为调整
src/utils/set.ts
在深度合并遍历对象键时,读取属性描述符并仅在属性为可枚举(descriptor.enumerable === true)时递归调用 merge,从而跳过非可枚举属性(保留数组处理、origin 初始化等既有分支逻辑)。

Sequence Diagram(s)

(无)

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~20 minutes

  • 重点检查 src/utils/set.ts 中对属性描述符的访问实现是否对所有运行时(浏览器、Node)安全,且不会在代理或跨 realm 对象上抛错。
  • 验证只跳过非可枚举属性是否会影响现有依赖非枚举字段的合并语义(尤其是原型/内建属性或被封装的第三方对象)。
  • 运行并通过新增测试 src/test/mergeWith.test.ts,并在稀疏数组、嵌套对象与带有非枚举属性的对象场景下额外确认行为。

Possibly related PRs

  • feat: support custom merge #711: 修改或重构了 src/utils/set.ts 中的合并逻辑,涉及 prepareArray 与深度合并机制,代码级相关性强。

Poem

🐰 我在代码地里跳,
可枚举才请你来跑,
稀疏数组不再闹,
测试通过胡萝卜笑,
合并轻轻,心里好。

Pre-merge checks and finishing touches

❌ Failed checks (1 warning)
Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 50.00% which is insufficient. The required threshold is 80.00%. You can run @coderabbitai generate docstrings to improve docstring coverage.
✅ Passed checks (2 passed)
Check name Status Explanation
Title check ✅ Passed 标题清晰准确地总结了主要变更:修复mergeWith中的数组长度截断问题并添加稀疏合并测试。
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
✨ Finishing touches
  • 📝 Generate docstrings
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@gemini-code-assist
Copy link

Summary of Changes

Hello @QDyanbing, I'm Gemini Code Assist1! I'm currently reviewing this pull request and will post my feedback shortly. In the meantime, here's a summary to help you and other reviewers quickly get up to speed!

此拉取请求修复了 mergeWith 工具函数中一个关键的数组合并问题。之前,当合并操作涉及数组时,如果源数组是稀疏的或长度较短,length 属性会被错误地处理,导致目标数组被截断并丢失元素。本次修复通过在遍历数组键时显式忽略 length 属性来解决此问题,确保了数组合并的正确性,并新增了测试用例以验证此行为。

Highlights

  • 修复 mergeWith 数组长度截断问题: 解决了 mergeWith 函数在合并稀疏数组或较短数组时,由于 Reflect.ownKeys 遍历到 length 属性并覆盖目标数组长度,导致元素丢失的问题。
  • 改进 mergeWith 数组键遍历逻辑: 在 mergeWith 函数处理数组时,现在会过滤掉 length 属性,只递归处理真实的索引键,确保数组的整体覆盖语义,同时不修改已有数组的长度。
  • 新增稀疏数组合并测试: 增加了一个新的测试用例 (mergeWith array merge),验证在合并稀疏更新时,mergeWith 能够正确保持现有数组的长度,避免截断。

🧠 New Feature in Public Preview: You can now enable Memory to help Gemini Code Assist learn from your team's feedback. This makes future code reviews more consistent and personalized to your project's style. Click here to enable Memory in your admin console.

Using Gemini Code Assist

The full guide for Gemini Code Assist can be found on our documentation page, here are some quick tips.

Invoking Gemini

You can request assistance from Gemini at any point by creating a comment using either /gemini <command> or @gemini-code-assist <command>. Below is a summary of the supported commands on the current page.

Feature Command Description
Code Review /gemini review Performs a code review for the current pull request in its current state.
Pull Request Summary /gemini summary Provides a summary of the current pull request in its current state.
Comment @gemini-code-assist Responds in comments when explicitly tagged, both in pull request comments and review comments.
Help /gemini help Displays a list of available commands.

Customization

To customize Gemini Code Assist for GitHub experience, repository maintainers can create a configuration file and/or provide a custom code review style guide (such as PEP-8 for Python) by creating and adding files to a .gemini/ folder in the base of the repository. Detailed instructions can be found here.

Limitations & Feedback

Gemini Code Assist may make mistakes. Please leave feedback on any instances where its feedback is incorrect or counter productive. You can react with 👍 and 👎 on @gemini-code-assist comments. If you're interested in giving your feedback about your experience with Gemini Code Assist for Github and other Google products, sign up here.

You can also get AI-powered code generation, chat, as well as code reviews directly in the IDE at no cost with the Gemini Code Assist IDE Extension.

Footnotes

  1. Review the Privacy Notices, Generative AI Prohibited Use Policy, Terms of Service, and learn how to configure Gemini Code Assist in GitHub here. Gemini can make mistakes, so double check it and use code with caution.

Copy link

@gemini-code-assist gemini-code-assist bot left a comment

Choose a reason for hiding this comment

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

Code Review

您好,这次的 Pull Request 做得很好。您准确地定位并修复了 mergeWith 函数在处理稀疏数组时可能导致数组被截断的 bug。通过在遍历数组键时过滤掉 length 属性,从根本上解决了问题。此外,新增的单元测试用例清晰地复现了该场景,能够有效防止未来发生类似回归问题。代码改动简洁明了,提高了工具函数的健壮性。我有一个关于代码实现的小建议,旨在略微提升性能和可读性,请查看具体评论。

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 0

🧹 Nitpick comments (1)
src/test/mergeWith.test.ts (1)

7-7: 可选:考虑注释语言的一致性

代码中包含中文注释 "仅更新第 2 项,长度为 2"。如果项目有注释语言规范,建议保持一致性。

🔎 可选的英文注释
-    changedValues.list[1] = 'BB'; // 仅更新第 2 项,长度为 2
+    changedValues.list[1] = 'BB'; // Only update index 1, length is 2
📜 Review details

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 233afca and 32e194e.

📒 Files selected for processing (2)
  • src/test/mergeWith.test.ts
  • src/utils/set.ts
🧰 Additional context used
🧬 Code graph analysis (1)
src/test/mergeWith.test.ts (1)
src/utils/set.ts (1)
  • mergeWith (79-129)
🔇 Additional comments (2)
src/utils/set.ts (1)

113-116: 修复正确,防止了数组长度被意外截断!

这个改动准确地解决了问题:当合并数组时,Reflect.ownKeys 会返回包括 'length' 在内的所有属性键。如果不过滤掉 'length',后续的稀疏数组或较短数组的 length 值会通过递归合并覆盖之前已合并的数组长度,导致元素丢失。

通过显式过滤 'length' 属性,确保只遍历真实的数组索引键,保留了数组的整体覆盖语义。

src/test/mergeWith.test.ts (1)

1-16: 测试用例有效验证了修复!

这个测试准确地覆盖了修复的场景:

  • 创建了一个稀疏数组(长度为 2,但只有索引 1 有值)
  • 使用 prepareArray 保留现有数组内容
  • 验证合并后的数组保持了原始长度 4,并且只更新了指定的索引

测试逻辑清晰,断言全面(同时检查了数组内容和长度)。

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 0

🧹 Nitpick comments (1)
src/utils/set.ts (1)

113-117: 考虑添加可选链以提高防御性,并评估性能优化的可能性。

这个解决方案相比硬编码检查 'length' 更加优雅和通用,能够自然地过滤掉所有非可枚举属性(包括数组的 length)。不过有两点可以考虑改进:

  1. 防御性编程:虽然在当前上下文中 Object.getOwnPropertyDescriptor(value, key) 理论上不会返回 undefined(因为 key 来自 keys(value)),但添加可选链会更安全:
-            if (Object.getOwnPropertyDescriptor(value, key).enumerable) {
+            if (Object.getOwnPropertyDescriptor(value, key)?.enumerable) {
  1. 性能优化:在不支持 Reflect 的环境中,keys 函数使用的是 Object.keys(第 67 行),它本身就只返回可枚举属性,此时这个检查是冗余的。可以考虑优化:
// 在文件顶部判断一次
const needsEnumerableCheck = typeof Reflect !== 'undefined';

// 在循环中
keys(value).forEach(key => {
  if (!needsEnumerableCheck || Object.getOwnPropertyDescriptor(value, key)?.enumerable) {
    internalMerge([...path, key], loopSet);
  }
});

不过考虑到现代环境基本都支持 Reflect,性能优化的收益可能有限。建议至少添加可选链以提高代码的健壮性。

📜 Review details

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 4550dd2 and a2f831c.

📒 Files selected for processing (1)
  • src/utils/set.ts

@zombieJ zombieJ merged commit d55c3ff into react-component:master Dec 25, 2025
7 of 8 checks passed
@codecov
Copy link

codecov bot commented Dec 25, 2025

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 91.58%. Comparing base (233afca) to head (a2f831c).
⚠️ Report is 1 commits behind head on master.

Additional details and impacted files
@@           Coverage Diff           @@
##           master     #714   +/-   ##
=======================================
  Coverage   91.57%   91.58%           
=======================================
  Files          37       37           
  Lines         938      939    +1     
  Branches      306      322   +16     
=======================================
+ Hits          859      860    +1     
  Misses         77       77           
  Partials        2        2           

☔ View full report in Codecov by Sentry.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

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.

Form.List 未包裹在 Form.Item 内时, Form 的 onValuesChange 回调中第二个参数 allValues 不正确

2 participants