-
Notifications
You must be signed in to change notification settings - Fork 1.4k
chore: upgrade to React 19 #9357
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: master
Are you sure you want to change the base?
Conversation
|
Note Other AI code review bot(s) detectedCodeRabbit 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. 📝 WalkthroughWalkthrough本PR升级React和相关依赖至19版本,统一refs初始化方式(添加explicit undefined),将ref类型从MutableRefObject改为RefObject,将JSX.Element统一改为React.ReactElement,并调整相应的类型断言。 Changes
Estimated code review effort🎯 3 (Moderate) | ⏱️ ~25 minutes 说明:PR涉及广泛的文件(100+),但改动模式高度同质化,主要为机械化的类型转换和ref初始化标准化。虽然总行数较多,但每个改动的复杂度低,且遵循一致的模式。需要关注的重点是确认ref类型从MutableRefObject改为RefObject的语义变化(从可变改为只读)是否符合意图,以及React 19兼容性。 Possibly related PRs
Suggested labels
Poem
🚥 Pre-merge checks | ✅ 2 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (2 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing touches
🧪 Generate unit tests (beta)
🧹 Recent nitpick comments
📜 Recent review detailsConfiguration used: Organization UI Review profile: CHILL Plan: Pro ⛔ Files ignored due to path filters (1)
📒 Files selected for processing (34)
🚧 Files skipped from review as they are similar to previous changes (22)
🧰 Additional context used📓 Path-based instructions (5)**/*.{tsx,jsx}📄 CodeRabbit inference engine (.cursor/rules/react.mdc)
Files:
**/*.{ts,tsx}📄 CodeRabbit inference engine (.cursor/rules/AGENTS.md)
Files:
**/*index.{ts,tsx}📄 CodeRabbit inference engine (.cursor/rules/AGENTS.md)
Files:
**/*.{tsx,ts}📄 CodeRabbit inference engine (.cursor/rules/AGENTS.md)
Files:
**/*.md📄 CodeRabbit inference engine (.cursor/rules/AGENTS.md)
Files:
🧠 Learnings (10)📓 Common learnings📚 Learning: 2025-12-26T06:32:24.230ZApplied to files:
📚 Learning: 2025-12-26T06:32:24.230ZApplied to files:
📚 Learning: 2025-12-26T06:32:24.230ZApplied to files:
📚 Learning: 2025-12-26T06:32:24.230ZApplied to files:
📚 Learning: 2025-12-26T06:32:24.230ZApplied to files:
📚 Learning: 2025-08-01T09:28:05.125ZApplied to files:
📚 Learning: 2025-12-26T06:32:24.230ZApplied to files:
📚 Learning: 2025-08-01T09:28:05.125ZApplied to files:
📚 Learning: 2025-12-26T06:32:24.230ZApplied to files:
🧬 Code graph analysis (2)demos/form/FieldSet/captCha.tsx (1)
src/form/layouts/LightFilter/index.tsx (1)
🔇 Additional comments (10)
✏️ Tip: You can disable this entire section by setting 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. Comment |
Summary of ChangesHello @zzjiaxiang, 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! 此拉取请求旨在将项目核心的 React 库升级到最新的 v19 版本。这包括更新 React 和 ReactDOM 的主要依赖项及其相应的类型定义,同时清理了不再需要的 Highlights
🧠 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 AssistThe 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
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 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
|
|
Review the following changes in direct dependencies. Learn more about Socket for GitHub.
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Code Review
感谢您升级 React 到 v19。这是一个重要的更新,移除 resolutions 字段也是正确的,因为 pnpm 使用 overrides。
不过,本次升级似乎还不完整,有几个关键点需要注意:
-
破坏性变更:React 19 引入了一些破坏性变更。例如,
React.FC不再隐式包含children属性。您的代码库中有多处使用了React.FC(如ProLayout.tsx),这可能会导致 TypeScript 编译错误。建议在合并前,对整个代码库进行检查和适配,可以考虑使用官方的 codemods 来辅助升级。 -
peerDependencies更新:作为组件库,package.json中的peerDependencies需要更新以反映对 React 19 的支持。这对于库的使用者至关重要,可以避免版本不兼容的问题。
在合并之前,请确保:
- 更新
peerDependencies来匹配新的 React 版本。 - 修复所有因 React 19 破坏性变更而导致的代码问题。
- 所有测试用例都能通过,并且
tsc编译没有错误。
| "react": "^19.2.3", | ||
| "react-dom": "^19.2.3", |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
There was a problem hiding this 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
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (1)
package.json (1)
147-147: 必须升级 eslint-plugin-react-hooks 到 v6+。React 19 要求
eslint-plugin-react-hooks使用 v6 或更高版本以支持新的 Hook 语义。当前版本 ^4.6.2 与 React 19 不兼容,可能导致 Hook 使用问题无法被正确检测。🔎 修复建议
- "eslint-plugin-react-hooks": "^4.6.2", + "eslint-plugin-react-hooks": "^6.0.0",根据 React 19.2 文档:React 19.2 requires eslint-plugin-react-hooks v6+ for new hook semantics.
📜 Review details
Configuration used: Organization UI
Review profile: CHILL
Plan: Pro
⛔ Files ignored due to path filters (1)
pnpm-lock.yamlis excluded by!**/pnpm-lock.yaml
📒 Files selected for processing (1)
package.json
🧰 Additional context used
🧠 Learnings (6)
📓 Common learnings
Learnt from: CR
Repo: ant-design/pro-components PR: 0
File: .cursor/rules/AGENTS.md:0-0
Timestamp: 2025-12-26T06:32:24.230Z
Learning: Support React 16 ~ 19 compatibility
📚 Learning: 2025-12-26T06:32:24.230Z
Learnt from: CR
Repo: ant-design/pro-components PR: 0
File: .cursor/rules/AGENTS.md:0-0
Timestamp: 2025-12-26T06:32:24.230Z
Learning: Support React 16 ~ 19 compatibility
Applied to files:
package.json
📚 Learning: 2025-12-26T06:32:24.230Z
Learnt from: CR
Repo: ant-design/pro-components PR: 0
File: .cursor/rules/AGENTS.md:0-0
Timestamp: 2025-12-26T06:32:24.230Z
Learning: Applies to **/*.test.{ts,tsx} : Use Jest and React Testing Library for unit tests
Applied to files:
package.json
📚 Learning: 2025-12-26T06:32:24.230Z
Learnt from: CR
Repo: ant-design/pro-components PR: 0
File: .cursor/rules/AGENTS.md:0-0
Timestamp: 2025-12-26T06:32:24.230Z
Learning: Support TypeScript 4.0+
Applied to files:
package.json
📚 Learning: 2025-12-26T06:32:24.230Z
Learnt from: CR
Repo: ant-design/pro-components PR: 0
File: .cursor/rules/AGENTS.md:0-0
Timestamp: 2025-12-26T06:32:24.230Z
Learning: Applies to **/*.{ts,tsx} : Use TypeScript and React for development
Applied to files:
package.json
📚 Learning: 2025-12-26T06:32:24.230Z
Learnt from: CR
Repo: ant-design/pro-components PR: 0
File: .cursor/rules/AGENTS.md:0-0
Timestamp: 2025-12-26T06:32:24.230Z
Learning: Applies to **/demo/**/*.tsx : Demo import order: React → Dependencies → Component Library → Custom Components → Types → Styles
Applied to files:
package.json
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (3)
- GitHub Check: build
- GitHub Check: build (20.x, ubuntu-latest)
- GitHub Check: Analyze (javascript)
🔇 Additional comments (3)
package.json (3)
200-201: 验证 peerDependencies 范围是否符合预期。当前 peerDependencies 指定
"react": ">=18.0.0",这在技术上包含了 React 19。但根据项目学习记录,该项目应支持 React 16 ~ 19 兼容性。如果项目确实需要支持 React 16+,peerDependencies 应该更新为:
"react": ">=16.0.0", "react-dom": ">=16.0.0"如果项目已决定仅支持 React 18+,则当前配置正确,但需要更新相关文档和学习记录。
基于项目学习记录:Support React 16 ~ 19 compatibility
请确认预期的 React 版本支持范围,并相应更新 peerDependencies 或文档。
173-174: React 运行时已正确升级到 v19.2.3,代码库完全兼容。扫描确认代码库中不存在 React 19 已移除的 API(propTypes、defaultProps、contextTypes、字符串 refs、createFactory 等)。ErrorBoundary 作为类组件实现是正确的,符合 React 规范。无需运行迁移 codemods,升级可直接使用。
119-120: eslint-plugin-react-hooks 版本过低,useRef 调用需要修复以支持 React 19。@types/react 和 @types/react-dom 类型定义已正确更新到 React 19。但存在两个需要立即修复的问题:
eslint-plugin-react-hooks 版本不兼容:当前版本 ^4.6.2 不支持 React 19 新增的 hooks(useActionState、useFormStatus、useOptimistic 等)。需升级至 ^6.0.0+。
useRef 调用存在 React 19 breaking change:发现 20+ 处 useRef() 不带初始值的调用需要修复,包括 src/utils/hooks/useDeepCompareEffect/index.ts:10、src/field/components/Radio/index.tsx:28 等。React 19 要求 useRef 必须提供初始值参数(可为 undefined)。
@testing-library/react ^16.3.1 版本已兼容 React 19,peerDependencies 的 >=18.0.0 范围也支持 React 19。建议运行 TypeScript 编译验证是否存在类型错误。
⛔ Skipped due to learnings
Learnt from: CR Repo: ant-design/pro-components PR: 0 File: .cursor/rules/AGENTS.md:0-0 Timestamp: 2025-12-26T06:32:24.230Z Learning: Support React 16 ~ 19 compatibilityLearnt from: CR Repo: ant-design/pro-components PR: 0 File: .cursor/rules/AGENTS.md:0-0 Timestamp: 2025-12-26T06:32:24.230Z Learning: Applies to **/*.{ts,tsx} : Use TypeScript and React for developmentLearnt from: CR Repo: ant-design/pro-components PR: 0 File: .cursor/rules/AGENTS.md:0-0 Timestamp: 2025-12-26T06:32:24.230Z Learning: Applies to **/*.test.{ts,tsx} : Use Jest and React Testing Library for unit testsLearnt from: CR Repo: ant-design/pro-components PR: 0 File: .cursor/rules/AGENTS.md:0-0 Timestamp: 2025-12-26T06:32:24.230Z Learning: Support TypeScript 4.0+
|
大部分都是类型的更改, 接受这个pr的话,我去修改类型错误. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Pull request overview
This PR upgrades React and React DOM from v18 to v19 and removes the yarn-specific resolutions field from package.json since the project uses pnpm.
Key Changes:
- React and React DOM upgraded from ^18.3.1/^18.3.27 to ^19.2.3/^19.2.7
- Type definitions upgraded: @types/react from ^18.0.38 to ^19.2.7, @types/react-dom from ^18.0.11 to ^19.2.3
- Removed
resolutionsfield (yarn-specific configuration, unnecessary for pnpm)
Reviewed changes
Copilot reviewed 1 out of 2 changed files in this pull request and generated no comments.
| File | Description |
|---|---|
| package.json | Updates React/React DOM to v19 and removes yarn-specific resolutions field |
| pnpm-lock.yaml | Reflects updated dependency tree with React 19 and all peer dependencies |
Review Summary:
The upgrade appears clean and consistent. All React-dependent packages in the lock file have been updated to use React 19 peer dependencies. The codebase uses React.FC extensively, which is compatible with React 19 (the deprecation only applies to React.FC.defaultProps, not the type itself). The defaultProps usage found in the code is for internal configuration objects, not the deprecated React component feature.
No critical issues identified. The removal of the resolutions field is appropriate for a pnpm-based project.
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
- Updated `react-markdown` version in `package.json`. - Added `actionRef` using `useRef` in multiple demo components and tests for better reference handling. - Updated documentation for `actionRef` in descriptions and form components. This enhances the functionality and maintainability of the components.
|
@coderabbitai review |
✅ Actions performedReview triggered.
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 19
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (3)
demos/table/form.tsx (1)
83-169: 严重错误:return 语句后存在不可达代码第 82 行的
return语句之后(第 83-169 行)存在大量 JSX 代码,这些代码永远不会被执行。这会导致文档说明无法渲染,并且是明显的逻辑错误。🔎 修复建议
将第 82 行的分号移除,并将整个 JSX 包裹在一个片段中:
return ( + <> <ProTable<TableListItem> columns={columns} request={() => Promise.resolve({ data: [ { key: 1, name: `TradeCode ${1}`, createdAt: 1602572994055, }, ], success: true, }) } rowKey="key" pagination={{ showSizeChanger: true, }} search={{ collapsed, onCollapse: setCollapsed, }} formRef={ref} toolBarRender={() => [ <Button key="set" onClick={() => { if (ref.current) { ref.current.setFieldsValue({ name: 'test-xxx', }); } }} > 赋值 </Button>, <Button key="submit" onClick={() => { if (ref.current) { ref.current.submit(); } }} > 提交 </Button>, ]} options={false} dateFormatter="string" headerTitle="表单赋值" /> - ); - <div + <div style={{ marginTop: '20px', padding: '20px', backgroundColor: '#f5f5f5', borderRadius: '6px', }} > {/* ...rest of the JSX... */} - </div>; + </div> + </> + );demos/table/single.tsx (1)
243-353: 严重错误:return 语句后存在不可达代码第 242 行的
return语句之后(第 243-353 行)存在大量 JSX 代码,这些代码永远不会被执行。这会导致 ProTable 的属性说明文档无法渲染。🔎 修复建议
将第 242 行的分号移除,并将整个 JSX 包裹在一个片段中:
return ( + <> <ProTable<GithubIssueItem> columns={columns} actionRef={actionRef} cardBordered request={async (params, sort, filter) => { console.log(sort, filter); await waitTime(2000); return request<{ data: GithubIssueItem[]; }>('https://proapi.azurewebsites.net/github/issues', { params, }); }} editable={{ type: 'multiple', }} columnsState={{ persistenceKey: 'pro-table-singe-demos', persistenceType: 'localStorage', defaultValue: { option: { fixed: 'right', disable: true }, }, onChange(value) { console.log('value: ', value); }, }} rowKey="id" search={{ labelWidth: 'auto', }} options={{ setting: { listsHeight: 400, }, }} form={{ // Since transform is configured, the submitted parameters are different from the defined ones, so they need to be transformed here syncToUrl: (values, type) => { if (type === 'get') { return { ...values, created_at: [values.startTime, values.endTime], }; } return values; }, }} pagination={{ pageSize: 5, onChange: (page) => console.log(page), }} dateFormatter="string" headerTitle="Advanced Table" toolBarRender={() => [ <Button key="button" icon={<PlusOutlined />} onClick={() => { actionRef.current?.reload(); }} type="primary" > New </Button>, <Dropdown key="menu" menu={{ items: [ { label: '1st item', key: '1', }, { label: '2nd item', key: '2', }, { label: '3rd item', key: '3', }, ], }} > <Button> <EllipsisOutlined /> </Button> </Dropdown>, ]} /> - ); - <div + <div style={{ marginTop: '20px', padding: '20px', backgroundColor: '#f5f5f5', borderRadius: '6px', }} > {/* ...rest of the JSX... */} - </div>; + </div> + </> + );src/form/components/FormItem/index.tsx (1)
51-60: 静态分析提示:void 函数中存在返回值。
onChangeMemo函数签名返回void,但第 54 行有return undefined;。这是一个轻微的类型不一致问题(非本 PR 引入)。类型转换
(filedChildren?.props as any)是 React 19 兼容性所需的正确做法。🔎 建议修复
const onChangeMemo = useRefFunction(function (...restParams: any[]): void { onChange?.(...restParams); if (isProFormComponent) return; - if (isValidElementForFiledChildren) return undefined; + if (isValidElementForFiledChildren) return; (filedChildren?.props as any)?.onChange?.(...restParams); (filedChildren?.props as Record<string, any>)?.fieldProps?.onChange?.( ...restParams, ); });
🧹 Nitpick comments (21)
src/utils/hooks/useDebounceFn/index.ts (1)
14-14: 符合 React 19 要求,但建议改进类型安全性。显式传入
undefined参数符合 React 19 的 useRef 要求,更改本身正确。但是:
- 类型改进:当前使用
any类型,建议改为更精确的类型。根据编码规范,应避免使用any,尽可能精确定义类型。- 值的一致性:初始化为
undefined,但在 line 19 清理时设置为null,建议统一使用null或undefined。🔎 建议的类型和一致性改进
- const timer = useRef<any>(undefined); + const timer = useRef<ReturnType<typeof setTimeout> | null>(null);相应地,在清理时保持使用
null(line 19 已经是null),这样初始值和清理值保持一致。说明:
ReturnType<typeof setTimeout>在 Node.js 环境返回NodeJS.Timeout,在浏览器环境返回number,提供了跨环境的类型安全性。根据编码规范要求,避免使用
any类型并精确定义类型。src/utils/hooks/useDeepCompareEffect/index.ts (1)
9-18: 可考虑为 ref 添加显式类型注解。虽然当前实现功能正常,但可以考虑为
ref添加显式类型注解以提升类型安全性,例如:const ref = useRef<typeof value | undefined>(undefined);这样可以更好地符合编码规范中"尽可能精确定义类型"的要求。
根据编码规范,应避免使用
any类型并尽可能精确定义类型。src/field/components/Cascader/index.tsx (1)
50-50: React 19 合规性变更正确。此变更符合 React 19 的要求,
useRef必须提供参数。运行时行为保持不变,因为 line 56 的cascaderRef.current || {}已正确处理 undefined 情况。建议添加显式类型注解以提升类型安全性和 IDE 支持:
🔎 建议添加显式类型注解
- const cascaderRef = useRef(undefined); + const cascaderRef = useRef<React.ElementRef<typeof Cascader> | undefined>(undefined);注:由于 PR 说明中提到会后续修复类型错误,此改进可以在类型修复阶段一并处理。
基于 learnings 和 React 19 文档要求。
src/layout/components/SettingDrawer/LayoutChange.tsx (1)
9-11: 避免使用as any类型断言,应定义精确的类型。使用
as any绕过了 TypeScript 的类型检查,降低了代码的类型安全性。根据项目编码规范,应该"避免使用any类型,尽可能精确地定义类型"。建议的改进方案:
- 方案一(推荐):为
action元素定义具体的 props 类型- 方案二:使用 React.cloneElement 的泛型参数明确类型
- 方案三:使用 React 19 官方提供的 codemod 工具处理类型迁移
基于编码规范:"Avoid using
anytype, define types as precisely as possible"🔎 推荐的类型安全改进方案
方案一:定义具体的 props 类型
export const renderLayoutSettingItem = (item: SettingItemProps) => { - const action = React.cloneElement(item.action, { + const action = React.cloneElement<React.ComponentProps<typeof item.action.type> & { disabled?: boolean }>( + item.action, + { - disabled: item.disabled, - } as any); + disabled: item.disabled, + } + ); return (方案二:使用更明确的类型断言
export const renderLayoutSettingItem = (item: SettingItemProps) => { const action = React.cloneElement(item.action, { disabled: item.disabled, - } as any); + } as Partial<React.ComponentProps<typeof item.action.type>>); return (src/field/components/Checkbox/index.tsx (1)
63-63: React 19 兼容性更改正确,建议添加显式类型参数。将
useRef()改为useRef(undefined)符合 React 19 的要求(useRef 现在必须提供参数)。不过建议添加显式类型参数以提高类型安全性。🔎 建议的类型改进
- const checkBoxRef = useRef(undefined); + const checkBoxRef = useRef<{ fetchData?: (keyWord: string) => void }>(undefined);可选:考虑移除未使用的 ref。
注意到
checkBoxRef从未被连接到任何组件(没有ref={checkBoxRef}传递给 Checkbox.Group),这意味着checkBoxRef.current始终为undefined,第 67 行的扩展操作实际上总是使用空对象。如果这个 ref 没有实际用途,可以考虑移除它以简化代码。根据学习记录,组件 ref 类型应该使用
React.ForwardRefRenderFunction显式定义。src/table/components/EditableTable/index.tsx (1)
137-152: 建议减少as any类型断言的使用。当前使用
as any访问children.props会丧失类型安全。根据编码指南,应尽量避免使用any类型。可以考虑为
children定义更精确的类型,或者使用类型守卫:🔎 可选的类型改进方案
function RecordCreator<T = Record<string, any>>( - props: RecordCreatorProps<T> & { children: React.ReactElement }, + props: RecordCreatorProps<T> & { + children: React.ReactElement<{ onClick?: (e: React.MouseEvent) => void | Promise<boolean | void> }> + }, ) { const { children, record, position, newRecordType, parentKey } = props; const actionRef = useContext(EditableTableActionContext); return React.cloneElement(children, { - ...(children.props as any), + ...children.props, onClick: async (e: any) => { // 如果返回了false,接触掉默认行为 - const isOk = await (children.props as any)?.onClick?.(e); + const isOk = await children.props?.onClick?.(e); if (isOk === false) return;不过考虑到
React.cloneElement的通用性和实际使用场景的多样性,当前的as any方案也是可接受的折中方案。src/utils/useEditableArray/index.tsx (1)
1109-1111: 类型定义更加精确。将
SaveEditableActionRef<any>添加| null使类型定义更准确,因为 ref 的current可能为null。代码在第 1158 行已经处理了这种情况(if (!saveRef?.current)),所以这个类型变更提高了类型安全性。💡 可选的优化建议
考虑将
any替换为更具体的泛型类型以提高类型安全性:-const saveRefsMap = useRef( - new Map<React.Key, React.RefObject<SaveEditableActionRef<any> | null>>(), -); +const saveRefsMap = useRef( + new Map<React.Key, React.RefObject<SaveEditableActionRef<RecordType> | null>>(), +);src/form/components/List/ListItem.tsx (1)
399-400: 建议改进类型安全性使用
as any类型断言绕过了类型检查,虽然这可能是为了兼容 React 19 中ReactElement["props"]从any变为unknown的改动,但违反了项目编码规范中"避免使用any类型"的要求。建议定义一个明确的接口来描述期望的 props 结构,例如:
interface ChildItemProps { name?: string | number; [key: string]: any; } // 然后使用 (childrenItem?.props as ChildItemProps)?.name或者使用类型守卫函数来更安全地访问
name属性。根据编码规范。
src/form/components/FormItem/Group/index.tsx (1)
101-104: 建议使用更精确的类型定义替代as any。使用
as any类型断言会完全绕过 TypeScript 的类型检查,降低了代码的类型安全性。建议定义一个包含hidden属性的接口来替代:🔎 建议的类型定义
在文件顶部添加接口定义:
+interface ProFormItemProps { + hidden?: boolean; + [key: string]: any; +}然后修改类型断言:
if ( React.isValidElement(element) && - (element?.props as any)?.hidden + (element?.props as ProFormItemProps)?.hidden ) {根据编码规范,应避免使用
any类型,尽可能精确定义类型。demos/table/EditableTable/form-linkage.tsx (1)
62-64: Ref 初始化正确,但建议改进泛型类型。三个 ref 的显式
undefined初始化符合 React 19 要求,与代码中的可选链用法一致。但 Line 62 的
ProFormInstance<any>使用了any类型。根据编码规范,应避免使用any,尽可能精确定义类型。🔎 建议的类型优化
由于表单已经定义了明确的类型结构(Line 173-175),建议使用精确类型:
+type FormValuesType = { + table: DataSourceType[]; +}; + -const formRef = useRef<ProFormInstance<any>>(undefined); +const formRef = useRef<ProFormInstance<FormValuesType>>(undefined);这样可以在使用
formRef.current?.getFieldValue()时获得更好的类型推断和类型安全。根据编码规范:避免使用
any类型,定义类型尽可能精确。src/layout/components/AppsLogoComponents/index.tsx (1)
55-55: 验证类型断言的必要性
popoverRef在第 48 行已经正确定义为React.useRef<HTMLSpanElement>(null),其类型应该已经是React.RefObject<HTMLSpanElement>。此处的类型断言可能是冗余的。建议验证是否真的需要这个类型断言,或者是否可以通过调整
onItemClick的类型定义来避免。🔎 如果类型断言确实是冗余的,可以考虑移除
const cloneItemClick = (app: AppItemProps) => { - itemClick?.(app, popoverRef as React.RefObject<HTMLSpanElement>); + itemClick?.(app, popoverRef); };src/layout/components/SettingDrawer/ThemeColor.tsx (1)
12-18: 考虑统一组件的 ref 处理方式。
ThemeColor组件已从ForwardRefRenderFunction简化为React.FC(符合 React 19 的改进),但同一文件中的Tag组件仍在使用React.forwardRef。为保持代码风格一致性,建议同步更新Tag组件。🔎 建议的 Tag 组件简化方案
-const Tag: React.FC<TagProps> = React.forwardRef( - ({ color, check, ...rest }, ref) => ( - <div {...rest} style={{ backgroundColor: color }} ref={ref as any}> +const Tag: React.FC<TagProps & { ref?: React.Ref<HTMLDivElement> }> = ({ + color, + check, + ref, + ...rest +}) => ( + <div {...rest} style={{ backgroundColor: color }} ref={ref}> {check ? <CheckOutlined /> : ''} </div> - ), -); + );Also applies to: 33-40
src/form/BaseForm/LightWrapper/index.tsx (1)
144-151: 考虑改进子元素的类型安全性。使用
as any类型断言降低了类型安全性。虽然这简化了代码,但可能在编译时遗漏潜在的类型错误。🔎 建议的类型安全改进
- {React.cloneElement(children as any, { + {React.isValidElement(children) && React.cloneElement(children, { ...rest, [valuePropName!]: tempValue, onChange: (e: any) => { setTempValue(e?.target ? e.target.value : e); }, - ...((children as any)?.props || {}), + ...(children.props || {}), })}此改进方案:
- 使用
React.isValidElement进行运行时检查- 移除不必要的类型断言
- 保持现有的空值安全处理
src/layout/components/GlobalHeader/ActionsContent.tsx (1)
75-75: 建议使用更精确的类型断言而非any。虽然此更改解决了 React 19 中
ReactElement["props"]默认为unknown的类型问题,但使用as any会削弱类型安全。由于aria-hidden是标准的 HTML 属性,建议使用更精确的类型断言。🔎 建议的改进方案
- hideHover = !!(dom?.props as any)?.['aria-hidden']; + hideHover = !!(dom?.props as React.HTMLAttributes<any>)?.['aria-hidden'];或者,如果需要支持更多属性类型:
- hideHover = !!(dom?.props as any)?.['aria-hidden']; + hideHover = !!((dom?.props as { 'aria-hidden'?: boolean })?.['aria-hidden']);src/field/FieldHOC/index.tsx (1)
44-47: 考虑使用更具体的类型而非any。将类型断言从
JSX.Element改为any会削弱类型安全性。虽然这样可以绕过 React 19 中JSX.Element类型定义的变更,但建议考虑使用React.ReactElement作为替代方案,以保持更好的类型检查。🔎 建议的类型改进
- {React.cloneElement(props.children as any, { + {React.cloneElement(props.children as React.ReactElement, { labelTrigger, lightLabel, })}或者,在 props 接口中明确定义 children 的类型:
function FieldHOC<T extends ProFieldLightProps>( props: T & { - children: React.ReactNode; + children: React.ReactElement; isLight?: boolean; }, ) {然后移除类型断言:
- {React.cloneElement(props.children as any, { + {React.cloneElement(props.children, { labelTrigger, lightLabel, })}src/form/layouts/ModalForm/index.tsx (1)
137-137: 考虑改进类型安全性。在 Lines 137 和 140 使用
as any类型断言会降低类型安全性,可能隐藏潜在的类型错误。🔎 建议的类型改进
考虑为 trigger props 定义更精确的类型:
+type TriggerProps = { + onClick?: (e: React.MouseEvent) => void; + [key: string]: any; +}; + const triggerDom = useMemo(() => { if (!trigger) { return null; } return React.cloneElement(trigger, { key: 'trigger', - ...(trigger.props as any), + ...(trigger.props as TriggerProps), onClick: async (e: any) => { setOpen(!open); - (trigger.props as any)?.onClick?.(e); + (trigger.props as TriggerProps)?.onClick?.(e); }, }); }, [setOpen, trigger, open]);Also applies to: 140-140
src/form/BaseForm/BaseForm.tsx (1)
423-423: 类型断言降低了类型安全性。使用
...(item.props as any)会绕过 TypeScript 的类型检查。🔎 建议的类型改进
考虑定义更精确的 props 类型:
const items = useMemo(() => { return React.Children.toArray(children as any).map((item, index) => { if (index === 0 && React.isValidElement(item) && autoFocusFirstInput) { return React.cloneElement(item, { - ...(item.props as any), + ...item.props, autoFocus: autoFocusFirstInput, - }); + } as React.ReactElement<any>); } return item; }); }, [autoFocusFirstInput, children]);或者,如果类型冲突是由于 React 19 的严格类型检查,可以添加注释说明原因。
src/layout/ProLayout.tsx (1)
106-109: 可选优化:logo 类型声明
logo类型中同时包含React.ReactNode和React.ReactElement。由于React.ReactElement是React.ReactNode的子集,这个联合类型可以简化。不过当前声明是有效的,不影响功能。可选的类型简化
logo?: | React.ReactNode - | React.ReactElement | WithFalse<() => React.ReactNode | React.ReactElement>;tests/field/field.test.tsx (3)
1465-1465: 建议使用更精确的类型替代any。根据编码规范,应避免使用
any类型。建议使用boolean类型,因为onOpenChange的参数应该是布尔值。🔎 建议的修改
- onOpenChange={(open: any) => fn(open)} + onOpenChange={(open: boolean) => fn(open)}
1630-1630: 建议使用更精确的类型。根据编码规范,应避免使用
any类型。🔎 建议的修改
- onChange={(value: any) => change(value)} + onChange={(value: string) => change(value)}
1927-1927: 建议使用更精确的类型。根据编码规范,应避免使用
any类型。可以定义具体的参数类型。🔎 建议的修改
- request={async (params: any) => { + request={async (params: { test?: string }) => {
| */ | ||
| export function useActionType<T>( | ||
| ref: React.MutableRefObject<ActionType | undefined>, | ||
| ref: React.RefObject<ActionType | undefined>, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
类型不匹配:RefObject 与可变赋值冲突
RefObject<T> 的 current 属性是只读的,但在第 136 行代码中直接对 ref.current 进行了赋值操作(ref.current = userAction)。这会导致 TypeScript 编译错误。
如果需要在函数内部修改 ref 的值,应该使用 React.MutableRefObject<ActionType | undefined> 而不是 React.RefObject。
🔎 建议的修复方案
export function useActionType<T>(
- ref: React.RefObject<ActionType | undefined>,
+ ref: React.MutableRefObject<ActionType | undefined>,
action: UseFetchDataAction<T>,
props: {
nativeElement?: HTMLDivElement;
focus?: () => void;
fullScreen: () => void;
onCleanSelected: () => void;
resetAll: () => void;
editableUtils: UseEditableUtilType;
/** 透传给 ActionType 的滚动能力 */
scrollTo?: ActionType['scrollTo'];
},
) {或者,如果确实要使用 RefObject,需要移除第 136 行的赋值操作,并调整架构以避免直接修改 ref。
🤖 Prompt for AI Agents
In src/table/utils/index.ts around lines 88–136, the function accepts ref:
React.RefObject<ActionType | undefined> but the code assigns to ref.current at
line 136, causing a TS error because RefObject.current is readonly; change the
parameter type to React.MutableRefObject<ActionType | undefined> (and import
MutableRefObject if needed) so current can be mutated, or alternatively remove
the assignment and refactor to use a callback ref or component state to expose
the ActionType without mutating a RefObject.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 11
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (4)
src/provider/index.tsx (1)
136-156: 应使用React.ReactNode保持类型一致性,而非React.ReactElement。项目的 peerDependencies 要求
react: >=18.0.0,并不支持 React 16,因此无需考虑 React 16 兼容性。然而,将
dom参数类型设为React.ReactElement存在一个问题:在相同项目中,其他类似的 render 方法(如SiderMenu和GlobalHeader中的render属性)均使用React.ReactNode作为 dom 参数类型。使用React.ReactElement比React.ReactNode更加严格,会限制开发者的灵活性(例如无法返回 Fragment 或字符串)。建议将
dom参数类型统一改为React.ReactNode,以保持整个代码库的一致性。src/table/components/Form/FormRender.tsx (1)
230-230: 类型错误:尝试赋值给只读属性第 230 行尝试给
formRef.current赋值,但formRef在第 106 行被定义为React.RefObject,其.current属性是只读的。这会导致 TypeScript 编译错误。建议的解决方案:
- 如果
formRef需要在内部赋值,考虑在组件内部使用独立的可变 ref,然后通过useImperativeHandle或其他机制同步- 或者检查是否可以移除这行赋值,因为
formRef已经在调用BetaSchemaForm时作为 prop 传递(第 226 行),表单组件应该自行管理 ref🔎 建议的修复方案
方案 1:使用内部可变 ref
const FormRender = <T, U = any>({ onSubmit, formRef, dateFormatter = 'string', type, columns, action, ghost, manualRequest, onReset, submitButtonLoading, search: searchConfig, form: formConfig, bordered, }: TableFormItem<T, U>) => { + const internalFormRef = useRef<ProFormInstance>(); const { hashId } = useContext(ProProvider); // ... rest of the code onInit={(values: T, form) => { - formRef.current = form; + internalFormRef.current = form; + // 如果外部 formRef 需要访问,可以使用其他方式同步 // 触发一个 submit,之所以这里触发是为了保证 value 都被 format了方案 2:移除赋值(如果 BetaSchemaForm 已经处理了 formRef)
onInit={(values: T, form) => { - formRef.current = form; // 触发一个 submit,之所以这里触发是为了保证 value 都被 format了src/layout/ProLayout.tsx (1)
245-250: 将RefObject改为MutableRefObject
actionRef的类型定义为React.RefObject,但第 539 行直接对actionRef.current进行赋值。由于RefObject的current属性被定义为readonly,在启用strict: true的 TypeScript 编译器下,这会导致类型错误:"Cannot assign to 'current' because it is a readonly property"。应将第 245 行的类型定义改为
React.MutableRefObject以支持可变的引用,或使用useImperativeHandle等 React Hooks 来管理 ref 的值。src/form/components/FormItem/index.tsx (1)
51-60: 修复返回类型声明与静态分析错误。该函数声明返回类型为
void,但第 54 行显式返回了undefined。虽然在 JavaScript 运行时void和undefined相似,但在 TypeScript 中这是不正确的。void类型表示函数不应返回任何值,而显式返回undefined违反了这一约定。此外,第 55、57 行使用
as any类型断言来访问filedChildren.props,这会绕过 TypeScript 的类型检查,可能导致运行时错误。🔎 建议的修复方案
- 移除显式的
return undefined语句:const onChangeMemo = useRefFunction(function (...restParams: any[]): void { onChange?.(...restParams); if (isProFormComponent) return; - if (isValidElementForFiledChildren) return undefined; + if (isValidElementForFiledChildren) return; (filedChildren?.props as any)?.onChange?.(...restParams);
- 考虑为
filedChildren定义更精确的类型,而不是使用as any:- (filedChildren?.props as any)?.onChange?.(...restParams); + if (React.isValidElement(filedChildren) && 'onChange' in filedChildren.props) { + (filedChildren.props as { onChange?: (...args: any[]) => void }).onChange?.(...restParams); + }根据静态分析工具提示。
🧹 Nitpick comments (18)
src/layout/components/GlobalHeader/ActionsContent.tsx (2)
74-76: 建议优化类型断言或添加注释说明。此更改添加了
as any类型断言以适配 React 19 中ReactElement["props"]默认为unknown的类型变化。虽然在处理来自消费者代码的任意元素时可能不可避免,但这降低了类型安全性。建议考虑以下选项之一:
- 使用更安全的类型检查方式:
const props = dom.props as Record<string, unknown>; hideHover = !!(props?.['aria-hidden']);
- 如果必须使用
as any,添加注释说明原因:// React 19: props 类型为 unknown,需要类型断言以访问任意属性 hideHover = !!(dom?.props as any)?.['aria-hidden'];请验证此模式是否与整体 React 19 升级策略一致。
基于编码规范:"避免使用
any类型,尽可能精确地定义类型"。
52-52: 建议为restParams定义精确类型。当前使用
any类型降低了类型安全性。建议基于GlobalHeaderProps定义一个精确的类型:type ActionsRenderParams = Omit<GlobalHeaderProps, 'avatarProps' | 'actionsRender' | 'headerContentRender'> & { rightContentSize?: number | string; }; const rightActionsRender = actionsRender || avatarDom ? (restParams: ActionsRenderParams) => { // ... } : undefined;基于编码规范:"避免使用
any类型,尽可能精确地定义类型"和"所有回调函数类型应明确定义参数和返回值"。src/field/components/Cascader/index.tsx (1)
50-50: 建议为 cascaderRef 添加显式类型声明虽然显式传入
undefined符合 React 19 要求,但与 PR 中其他文件不同,此处未提供类型参数。建议添加显式类型以提高类型安全性和代码可维护性。🔎 建议的类型改进
- const cascaderRef = useRef(undefined); + const cascaderRef = useRef<any>(undefined);或者,如果能获取到 Cascader 组件的 ref 类型,使用更精确的类型:
const cascaderRef = useRef<React.ElementRef<typeof Cascader>>(undefined);根据代码规范,组件 ref 类型应当显式定义。
demos/form/FieldSet/captCha.tsx (1)
14-15: 建议添加明确的类型参数以提升类型安全这两个 ref 缺少类型参数,导致在使用时需要
// @ts-ignore注释(Lines 27, 37)。建议为 ref 添加适当的类型定义以消除类型忽略并提升代码质量。🔎 建议的类型改进
+// 定义 captcha ref 的类型接口 +interface CaptchaFieldRef { + startTiming: () => void; + endTiming: () => void; +} + const Demo = () => { - const captchaRef = useRef(undefined); - const inputRef = useRef(undefined); + const captchaRef = useRef<CaptchaFieldRef>(undefined); + const inputRef = useRef<HTMLInputElement>(undefined); return ( <ProForm title="新建表单" submitter={{ render: (props, doms) => { return [ ...doms, <Button htmlType="button" onClick={() => { - // @ts-ignore captchaRef.current?.startTiming(); }} key="edit" > 手动开始计数 </Button>, <Button htmlType="button" onClick={() => { - // @ts-ignore captchaRef.current?.endTiming(); }} key="edit2" > 手动结束计数 </Button>, ]; }, }}demos/form/FieldSet/pro-form-captCha.tsx (1)
16-16: 建议为 inputRef 添加显式类型注解Line 16 的
inputRef初始化缺少类型参数,TypeScript 会将其推断为React.MutableRefObject<undefined>,这可能不是预期的类型。由于该 ref 通过fieldProps.ref传递给输入组件(Line 64),建议添加显式的 HTML 元素类型注解。🔎 建议的类型注解改进
- const inputRef = useRef(undefined); + const inputRef = useRef<HTMLInputElement>(undefined);src/utils/useEditableArray/index.tsx (1)
1117-1117: 说明忽略 ref 值的原因useEffect 的迭代从
(ref, key)改为(_, key),不再使用 ref 值。虽然当前逻辑中确实不需要 ref 值(仅用于删除 key),但建议添加注释说明为何忽略该参数,以提高代码可读性。🔎 建议的注释
- saveRefsMap.current.forEach((_, key) => { + // 只需要 key 来判断是否需要删除,ref 值在此处不使用 + saveRefsMap.current.forEach((_, key) => {src/form/components/FormItem/Group/index.tsx (1)
101-104: 避免使用as any绕过类型检查使用
(element?.props as any)?.hidden会绕过 TypeScript 的类型安全检查。建议使用类型守卫或更精确的类型定义来访问hidden属性。🔎 推荐的重构方案
方案一:使用类型守卫
+const hasHiddenProp = ( + element: React.ReactElement +): element is React.ReactElement<{ hidden?: boolean }> => { + return 'hidden' in (element.props || {}); +}; + const [childrenDoms, hiddenDoms] = useMemo(() => { const hiddenChildren: React.ReactNode[] = []; const childrenList = React.Children.toArray(children).map( (element, index) => { if ( React.isValidElement(element) && - (element?.props as any)?.hidden + hasHiddenProp(element) && + element.props.hidden ) { hiddenChildren.push(element); return null; }方案二:使用 in 运算符检查
if ( React.isValidElement(element) && - (element?.props as any)?.hidden + 'hidden' in (element?.props || {}) && + (element.props as { hidden?: boolean }).hidden ) {src/form/components/List/ListItem.tsx (1)
399-400: 建议使用更具体的类型而非any。使用
as any绕过了 React 19 中更严格的类型检查(ReactElement["props"]现在默认为unknown)。虽然这能正常工作,但牺牲了类型安全性。🔎 建议的改进方案
如果子元素的 props 结构是已知的(比如包含
name属性的表单字段),可以定义一个接口并使用更精确的类型断言:+interface ChildPropsWithName { + name?: string | number; + [key: string]: any; +} + .map((childrenItem, itemIndex) => { if (React.isValidElement(childrenItem)) { return React.cloneElement(childrenItem, { key: - childrenItem.key || (childrenItem?.props as any)?.name || itemIndex, + childrenItem.key || (childrenItem?.props as ChildPropsWithName)?.name || itemIndex, ...(childrenItem?.props || {}), }); }或者,如果
name属性的存在性需要运行时检查:.map((childrenItem, itemIndex) => { if (React.isValidElement(childrenItem)) { + const childProps = childrenItem?.props as Record<string, unknown>; + const keyFromProps = typeof childProps?.name === 'string' || typeof childProps?.name === 'number' + ? childProps.name + : undefined; return React.cloneElement(childrenItem, { key: - childrenItem.key || (childrenItem?.props as any)?.name || itemIndex, + childrenItem.key || keyFromProps || itemIndex, ...(childrenItem?.props || {}), }); }src/layout/components/SettingDrawer/LayoutChange.tsx (1)
9-11: 建议使用更安全的类型断言方式。当前使用
as any完全绕过了类型检查,可能会隐藏潜在的类型错误。建议考虑以下更安全的替代方案:
- 使用条件类型检查 action 是否支持 disabled 属性
- 定义明确的 action 类型约束,确保所有 action 元素都支持 disabled
🔎 建议的类型安全改进
export const renderLayoutSettingItem = (item: SettingItemProps) => { const action = React.cloneElement(item.action, { disabled: item.disabled, - } as any); + } as React.HTMLAttributes<any>); return (或者,在 SettingItemProps 接口中明确定义 action 的类型:
interface SettingItemProps { action: React.ReactElement<{ disabled?: boolean }>; disabled?: boolean; // ... 其他属性 }src/form/layouts/DrawerForm/index.tsx (1)
183-191: 建议改进类型断言的使用在克隆
trigger元素时,使用了as any类型断言来访问 props(第 185 和 189 行)。虽然这在动态元素克隆场景中可以工作,但会降低类型安全性。考虑定义一个更具体的类型来替代
any,例如:type TriggerProps = { onClick?: (e: any) => void; [key: string]: any; };然后使用
trigger.props as TriggerProps,这样可以在保持灵活性的同时提供基本的类型检查。src/form/layouts/QueryFilter/index.tsx (1)
245-245: 类型更新正确,但类型断言使用频繁
submitter属性类型更新为React.ReactElement | false符合 React 19 要求。但在多处使用as any访问 props(第 311、334、400 行)降低了类型安全性。由于这些组件处理动态的 React 元素和子元素,完全的类型安全可能难以实现。但建议考虑:
- 为常用的 props(如
colSize、name、hidden)定义接口- 使用联合类型而不是完全放弃类型检查
这与 DrawerForm 中的模式一致,表明这是项目范围内的权衡取舍。
Also applies to: 311-311, 334-334, 400-400
src/table/components/EditableTable/index.tsx (1)
132-132: 建议改进类型安全性将
children类型从JSX.Element更改为React.ReactElement是正确的,但随后使用as any类型断言绕过了类型检查,这可能会隐藏潜在的类型错误。建议定义更精确的类型:
🔎 建议的类型改进
function RecordCreator<T = Record<string, any>>( - props: RecordCreatorProps<T> & { children: React.ReactElement }, + props: RecordCreatorProps<T> & { + children: React.ReactElement<{ onClick?: (e: any) => void | boolean | Promise<void | boolean> }> + }, ) { const { children, record, position, newRecordType, parentKey } = props; const actionRef = useContext(EditableTableActionContext); return React.cloneElement(children, { - ...(children.props as any), + ...children.props, onClick: async (e: any) => { // 如果返回了false,接触掉默认行为 - const isOk = await (children.props as any)?.onClick?.(e); + const isOk = await children.props?.onClick?.(e); if (isOk === false) return; if (actionRef?.current) { actionRef.current.addEditRecord(record as any, { position, newRecordType, parentKey: parentKey as React.Key, }); } }, }); }Also applies to: 138-138, 141-141
tests/field/field.test.tsx (1)
648-648: 测试中的类型注解为测试回调函数添加显式类型注解以满足 React 19 更严格的类型检查。虽然使用了
any类型,但在测试代码中这是可以接受的权衡。如果时间允许,可以考虑使用更具体的类型来提高类型安全性,但这不是必需的。
可选的类型改进示例
例如对于 Line 1630,可以使用更具体的类型:
- onChange={(value: any) => change(value)} + onChange={(value: string | number | undefined) => change(value)}Also applies to: 1465-1465, 1489-1489, 1554-1554, 1630-1630, 1927-1927
src/form/layouts/StepsForm/index.tsx (1)
129-133: 建议移除as any类型断言。在
React.cloneElement中使用as any会抑制类型检查,可能会掩盖真正的类型错误。建议明确指定正确的 props 类型,或者验证style属性是否与stepsDom的类型兼容。🔎 建议的改进方案
移除类型断言并显式类型化 props:
- {React.cloneElement(stepsDom, { - style: { - height: '100%', - }, - } as any)} + {React.cloneElement(stepsDom, { + style: { + height: '100%', + }, + })}如果遇到类型错误,请根据实际的
stepsDom类型(应该是React.ReactElement)来调整。src/form/components/FormItem/index.tsx (3)
62-70: 建议改进类型安全性。第 66 行使用
as any类型断言访问filedChildren.props.onBlur,这会绕过类型检查。建议使用类型守卫或更精确的类型定义来替代as any。🔎 建议的改进方案
- (filedChildren?.props as any)?.onBlur?.(...restParams); + if (React.isValidElement(filedChildren) && 'onBlur' in filedChildren.props) { + (filedChildren.props as { onBlur?: (...args: any[]) => void }).onBlur?.(...restParams); + }
123-133: 建议移除 props 展开时的类型断言。第 128 行在展开
filedChildren.props时使用了as any断言。如果filedChildren是React.ReactElement,其props应该已有正确的类型,无需使用as any。这可能会掩盖 props 类型不兼容的问题。🔎 建议的改进方案
return React.cloneElement( filedChildren, omitUndefined({ ...restProps, [valuePropName]: formFieldProps[valuePropName], - ...(filedChildren.props as any), + ...filedChildren.props, onChange: finalChange, fieldProps, onBlur: isProFormComponent && !isValidElementForFiledChildren && onBlur, }), );如果遇到类型错误,应该明确定义
filedChildren的类型,而不是使用as any绕过。
370-372: 建议改进 valueType 访问的类型安全性。第 372 行使用
as any断言访问props.children.props.valueType。建议添加类型守卫以确保valueType属性存在且类型正确。🔎 建议的改进方案
const isDropdown = React.isValidElement(props.children) && - isDropdownValueType(valueType || (props.children.props as any).valueType); + isDropdownValueType( + valueType || + (React.isValidElement(props.children) && + 'valueType' in props.children.props ? + (props.children.props as { valueType?: unknown }).valueType : + undefined) + );src/field/components/Radio/index.tsx (1)
28-28: React 19 兼容性改动正确。将
useRef()改为useRef(undefined)符合 React 19 的要求(useRef 现在需要参数)。虽然可以添加显式类型注解以提高类型安全性,但当前模式在代码库中一致使用(如 Cascader、Checkbox、Select 等字段组件),且 TypeScript 能从 Radio.Group 的使用推断出类型。如果要统一类型注解风格,可参考已有的模式:- const radioRef = useRef(undefined); + const radioRef = useRef<React.ElementRef<typeof Radio.Group> | undefined>(undefined);不过此改动非必需。
|
|
||
| /** 一些简单的操作 */ | ||
| actionRef?: React.MutableRefObject<ProCoreActionType<any> | undefined>; | ||
| actionRef?: React.RefObject<ProCoreActionType<any> | undefined>; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🧩 Analysis chain
🏁 Script executed:
#!/bin/bash
# First, check the file size and structure
wc -l src/descriptions/index.tsxRepository: ant-design/pro-components
Length of output: 99
🏁 Script executed:
#!/bin/bash
# Read the relevant sections mentioned in the review comment
echo "=== Line 101 (actionRef definition) ==="
sed -n '95,110p' src/descriptions/index.tsx
echo -e "\n=== Lines 527-532 (actionRef assignment) ==="
sed -n '520,540p' src/descriptions/index.tsx
echo -e "\n=== Lines 323, 391, 449, 452 (other mentioned changes) ==="
sed -n '320,330p' src/descriptions/index.tsx
sed -n '388,395p' src/descriptions/index.tsx
sed -n '445,455p' src/descriptions/index.tsxRepository: ant-design/pro-components
Length of output: 2038
🏁 Script executed:
#!/bin/bash
# Search for all actionRef assignments in the file to understand usage patterns
rg -n 'actionRef' src/descriptions/index.tsx -A 2 -B 2Repository: ant-design/pro-components
Length of output: 739
🏁 Script executed:
#!/bin/bash
# Check for any type casting or suppression around the actionRef assignment
sed -n '525,535p' src/descriptions/index.tsx | cat -nRepository: ant-design/pro-components
Length of output: 369
🏁 Script executed:
#!/bin/bash
# Search for ProCoreActionType definition to understand the type structure
rg -n 'type ProCoreActionType|interface ProCoreActionType' --type=ts --type=tsxRepository: ant-design/pro-components
Length of output: 96
🏁 Script executed:
#!/bin/bash
# Check if there's a useRef hook being used and how actionRef is passed in
rg -n 'useRef.*actionRef|actionRef.*useRef' src/descriptions/ --type=ts --type=tsx -B 2 -A 2Repository: ant-design/pro-components
Length of output: 96
🏁 Script executed:
#!/bin/bash
# Search for ProCoreActionType definition
rg -n 'ProCoreActionType' --type ts -A 5 | head -50Repository: ant-design/pro-components
Length of output: 2597
🏁 Script executed:
#!/bin/bash
# Search with broader pattern for the type definition
rg 'type ProCoreActionType|interface ProCoreActionType' -A 5Repository: ant-design/pro-components
Length of output: 403
🏁 Script executed:
#!/bin/bash
# Check how actionRef is being used as a parameter in the component
sed -n '475,495p' src/descriptions/index.tsx | cat -nRepository: ant-design/pro-components
Length of output: 546
🏁 Script executed:
#!/bin/bash
# Look at the complete function signature to understand the parameters
head -120 src/descriptions/index.tsx | tail -50Repository: ant-design/pro-components
Length of output: 1223
🏁 Script executed:
#!/bin/bash
# Check if there are any type assertions or workarounds around the actionRef assignment
sed -n '525,535p' src/descriptions/index.tsxRepository: ant-design/pro-components
Length of output: 292
🏁 Script executed:
#!/bin/bash
# Look at the complete usage to see if actionRef is cast to MutableRefObject anywhere
rg -n 'as.*MutableRefObject|as.*Ref' src/descriptions/index.tsx -B 2 -A 2Repository: ant-design/pro-components
Length of output: 51
🏁 Script executed:
#!/bin/bash
# Check the tsconfig to see if there are any type checking relaxations
cat tsconfig.json | grep -A 10 '"compilerOptions"'Repository: ant-design/pro-components
Length of output: 378
🏁 Script executed:
#!/bin/bash
# Check if useImperativeHandle or other patterns are used
rg -n 'useImperativeHandle|forwardRef' src/descriptions/index.tsx -B 2 -A 5Repository: ant-design/pro-components
Length of output: 51
🏁 Script executed:
#!/bin/bash
# Check the complete hook signature and return to understand the pattern better
sed -n '470,510p' src/descriptions/index.tsx | cat -nRepository: ant-design/pro-components
Length of output: 1249
🏁 Script executed:
#!/bin/bash
# Verify if this is causing any actual TypeScript errors by checking if the file compiles
# Look for any build output or type checking results
rg -n 'MutableRefObject' src/descriptions/index.tsxRepository: ant-design/pro-components
Length of output: 51
actionRef.current 赋值违反 RefObject 的只读约束
React.RefObject<T> 的 current 属性是只读的(readonly),但第 528 行直接对其赋值,这在启用 strict: true 的 TypeScript 严格模式下会产生编译错误。
需要解决此兼容性问题:
- 如果要支持赋值操作,应恢复使用
React.MutableRefObject<ProCoreActionType<any> | undefined> - 或重构 useEffect 中的逻辑以避免直接修改 current 属性
🤖 Prompt for AI Agents
In src/descriptions/index.tsx around line 101 (prop declaration) and the
assignment at line 528, the code assigns to actionRef.current which violates the
readonly contract of React.RefObject; either change the prop type to
React.MutableRefObject<ProCoreActionType<any> | undefined> so current is
writable, or refactor the effect to avoid direct assignment (e.g., create and
use a local mutable ref or expose a setter/callback prop and assign via that).
Update the prop type and any callers to accept MutableRefObject if choosing the
first option, or rewrite the useEffect to set state/call a setter instead of
mutating current if choosing the second.
| {React.cloneElement(props.children as any, { | ||
| labelTrigger, | ||
| lightLabel, | ||
| })} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🛠️ Refactor suggestion | 🟠 Major
避免使用 any 类型断言,建议改用 React.ReactElement
将类型断言从 JSX.Element 改为 any 会失去类型安全性。根据 React 19 的类型系统更新,建议使用 React.ReactElement 代替 any。
🔎 建议的类型安全重构
- {React.cloneElement(props.children as any, {
+ {React.cloneElement(props.children as React.ReactElement, {
labelTrigger,
lightLabel,
})}根据编码规范:应尽量避免使用 any 类型,尽可能精确地定义类型。
📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| {React.cloneElement(props.children as any, { | |
| labelTrigger, | |
| lightLabel, | |
| })} | |
| {React.cloneElement(props.children as React.ReactElement, { | |
| labelTrigger, | |
| lightLabel, | |
| })} |
🤖 Prompt for AI Agents
In src/field/FieldHOC/index.tsx around lines 44 to 47, replace the unsafe "any"
type assertion when cloning children: ensure props.children is typed as
React.ReactElement (or React.ReactElement<PropsType> if you can specify the
child's prop types) and narrow-check it before calling React.cloneElement so
TypeScript knows it's an element; then call React.cloneElement(props.children as
React.ReactElement, { labelTrigger, lightLabel }) (or use a typed generic) to
preserve type safety and avoid any.
| if (index === 0 && React.isValidElement(item) && autoFocusFirstInput) { | ||
| return React.cloneElement(item, { | ||
| ...item.props, | ||
| ...(item.props as any), |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
类型断言降低了类型安全性。
使用 as any 来绕过类型检查会削弱 TypeScript 的类型保护。虽然这可能是 React 19 类型更新的临时解决方案,但建议:
- 如果
item.props有明确的类型定义,应使用更具体的类型断言 - 或者定义一个适当的接口来描述预期的 props 结构
🔎 可能的改进方案
- ...(item.props as any),
+ ...item.props,如果类型错误是由于 React 19 的 ReactElement 类型更严格导致的,可以考虑定义更精确的类型:
type ValidElementProps = {
autoFocus?: boolean;
[key: string]: any;
};
// 然后使用
...(item.props as ValidElementProps),基于编码指南,应避免使用 any 类型并尽可能精确地定义类型。
📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| ...(item.props as any), | |
| ...item.props, |
🤖 Prompt for AI Agents
In src/form/BaseForm/BaseForm.tsx around line 423, change the unsafe "as any"
assertion when spreading item.props to a precise type: determine the expected
props shape (either define a ValidElementProps interface with explicit optional
fields like autoFocus and an index signature, or derive the props type from the
element type using React.ComponentPropsWithoutRef<typeof item.type> /
React.ComponentProps<typeof item.type>), replace "(item.props as any)" with a
cast to that specific type, and update imports/types accordingly so the spread
preserves type safety instead of using any.
| ...(trigger.props as any), | ||
| onClick: async (e: any) => { | ||
| setOpen(!open); | ||
| trigger.props?.onClick?.(e); | ||
| (trigger.props as any)?.onClick?.(e); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
多处类型断言削弱了类型安全保障。
在克隆 trigger 元素时使用了两次 as any 断言(line 137 和 line 140),这会绕过 TypeScript 的类型检查。考虑以下改进:
- 定义明确的 trigger props 接口
- 使用更具体的类型而非
any - 利用
React.ReactElement的类型参数
🔎 建议的类型安全改进
+type TriggerProps = {
+ onClick?: (e: React.MouseEvent) => void;
+ [key: string]: any;
+};
+
return React.cloneElement(trigger, {
key: 'trigger',
- ...(trigger.props as any),
+ ...trigger.props,
onClick: async (e: any) => {
setOpen(!open);
- (trigger.props as any)?.onClick?.(e);
+ trigger.props.onClick?.(e);
},
});基于编码指南,应避免使用 any 类型,并尽可能精确地定义类型。
📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| ...(trigger.props as any), | |
| onClick: async (e: any) => { | |
| setOpen(!open); | |
| trigger.props?.onClick?.(e); | |
| (trigger.props as any)?.onClick?.(e); | |
| type TriggerProps = { | |
| onClick?: (e: React.MouseEvent) => void; | |
| [key: string]: any; | |
| }; | |
| return React.cloneElement(trigger, { | |
| key: 'trigger', | |
| ...trigger.props, | |
| onClick: async (e: any) => { | |
| setOpen(!open); | |
| trigger.props.onClick?.(e); | |
| }, | |
| }); |
🤖 Prompt for AI Agents
In src/form/layouts/ModalForm/index.tsx around lines 137-140, the code weakens
type safety by using two `as any` assertions when cloning the trigger element;
replace these with a properly typed approach: declare an explicit TriggerProps
interface (including optional onClick: (e: React.MouseEvent) => void and any
other used props), type the trigger as React.ReactElement<TriggerProps>, and
when cloning cast to that specific type (or use generics) instead of `any`; call
the typed onClick handler and spread the strongly typed props so TypeScript can
check correctness.
|
|
||
| export function useDeepCompareMemoize(value: any, ignoreKeys?: any) { | ||
| const ref = useRef(); | ||
| const ref = useRef(undefined); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
需要添加类型参数以确保正确的类型推断。
当前 useRef(undefined) 会被 TypeScript 推断为 MutableRefObject<undefined>。虽然第 14 行可以将 any 类型的 value 赋值给 ref.current,但 ref.current 的类型仍然是 undefined,导致第 17 行的返回类型被推断为 undefined 而不是预期的 any,这会引发类型错误。
🔎 建议的修复方案
方案 1(快速修复): 显式指定类型参数
- const ref = useRef(undefined);
+ const ref = useRef<any>(undefined);方案 2(推荐): 使用泛型实现类型安全
-export function useDeepCompareMemoize(value: any, ignoreKeys?: any) {
- const ref = useRef(undefined);
+export function useDeepCompareMemoize<T>(value: T, ignoreKeys?: any) {
+ const ref = useRef<T | undefined>(undefined);
// it can be done by using useMemo as well
// but useRef is rather cleaner and easier
if (!isDeepEqual(value, ref.current, ignoreKeys)) {
ref.current = value;
}
return ref.current;
}方案 2 更符合编码规范中"避免使用 any 类型"和"适当使用泛型以增强类型灵活性"的要求。
Committable suggestion skipped: line range outside the PR's diff.
🤖 Prompt for AI Agents
In src/utils/hooks/useDeepCompareEffect/index.ts around line 10, the declaration
const ref = useRef(undefined) lets TypeScript infer MutableRefObject<undefined>,
causing downstream types to be wrong; fix by adding explicit generics: make the
hook generic (e.g. function useDeepCompareEffect<T = any>(value: T, ...)) and
change the ref to useRef<T | undefined>(undefined) (or useRef<T>(undefined as
unknown as T) if necessary) so ref.current can accept the incoming value and the
return type is correctly inferred; update any related type annotations to use
the generic parameter instead of any.
| const saveRefsMap = useRef( | ||
| new Map<React.Key, React.RefObject<SaveEditableActionRef<any> | null>>(), | ||
| ); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🧩 Analysis chain
🏁 Script executed:
# First, let's read the relevant section of the file to see the context
wc -l src/utils/useEditableArray/index.tsxRepository: ant-design/pro-components
Length of output: 110
🏁 Script executed:
# Read lines around 1109-1111 to see the saveRefsMap definition
sed -n '1100,1120p' src/utils/useEditableArray/index.tsxRepository: ant-design/pro-components
Length of output: 648
🏁 Script executed:
# Find all usages of saveRefsMap to check how it's being used
rg -n "saveRefsMap" src/utils/useEditableArray/index.tsxRepository: ant-design/pro-components
Length of output: 422
🏁 Script executed:
# Check line 1158 and surrounding context to see the saveRef?.current usage
sed -n '1150,1170p' src/utils/useEditableArray/index.tsxRepository: ant-design/pro-components
Length of output: 456
🏁 Script executed:
# Find the SaveEditableActionRef type definition
rg -n "SaveEditableActionRef" src/utils/useEditableArray/index.tsx | head -20Repository: ant-design/pro-components
Length of output: 427
🏁 Script executed:
# Find the getSaveRef function definition
rg -n "getSaveRef" src/utils/useEditableArray/index.tsxRepository: ant-design/pro-components
Length of output: 187
🏁 Script executed:
# Check the context around lines 1130-1131 where saveRefsMap.current.get is used
sed -n '1125,1145p' src/utils/useEditableArray/index.tsxRepository: ant-design/pro-components
Length of output: 757
🏁 Script executed:
# Check the SaveEditableActionRef type definition at line 486
sed -n '486,510p' src/utils/useEditableArray/index.tsxRepository: ant-design/pro-components
Length of output: 509
🏁 Script executed:
# Check where saveRefsMap.current.set is called (lines 1420, 1426) to see what values are stored
sed -n '1415,1435p' src/utils/useEditableArray/index.tsxRepository: ant-design/pro-components
Length of output: 744
🏁 Script executed:
# Check if there's any explicit null assignment to saveRefsMap
rg -n "saveRefsMap.*null" src/utils/useEditableArray/index.tsxRepository: ant-design/pro-components
Length of output: 51
🏁 Script executed:
# Get more context around line 1157 to see how saveRef is used after getSaveRef
sed -n '1155,1175p' src/utils/useEditableArray/index.tsxRepository: ant-design/pro-components
Length of output: 497
🏁 Script executed:
# Search for all references to the result of getSaveRef and how it's used
rg -n "getSaveRef\(" src/utils/useEditableArray/index.tsx -A5Repository: ant-design/pro-components
Length of output: 241
🏁 Script executed:
# Check the defaultActionRender function to understand what renderResult.saveRef is
rg -n "defaultActionRender" src/utils/useEditableArray/index.tsx -B5 -A10 | head -40Repository: ant-design/pro-components
Length of output: 1134
🏁 Script executed:
# Let's check the defaultActionRender return type to understand what renderResult.saveRef is
sed -n '604,630p' src/utils/useEditableArray/index.tsxRepository: ant-design/pro-components
Length of output: 832
🏁 Script executed:
# Let's verify what createRef returns in React
rg -n "createRef" src/utils/useEditableArray/index.tsxRepository: ant-design/pro-components
Length of output: 145
🏁 Script executed:
# Check if there's a type definition for the return value of defaultActionRender
rg -n "return \{" src/utils/useEditableArray/index.tsx | grep -A20 "defaultActionRender"Repository: ant-design/pro-components
Length of output: 51
🏁 Script executed:
# Check if there are any explicit null assignments or patterns in the whole file
rg -n "saveRefsMap.*=.*null|null.*saveRefsMap" src/utils/useEditableArray/index.tsxRepository: ant-design/pro-components
Length of output: 51
🏁 Script executed:
# Let's check the import statements to understand React.RefObject better
sed -n '1,30p' src/utils/useEditableArray/index.tsxRepository: ant-design/pro-components
Length of output: 1117
🏁 Script executed:
# Check all Map operations on saveRefsMap to see if null is ever intentionally stored
rg -n "\.set\(" src/utils/useEditableArray/index.tsx | grep -A2 -B2 "saveRefsMap"Repository: ant-design/pro-components
Length of output: 281
🏁 Script executed:
# Let's check the complete context of how getSaveRef result is used elsewhere
rg -n "const.*saveRef.*getSaveRef\|getSaveRef.*current" src/utils/useEditableArray/index.tsx -A3Repository: ant-design/pro-components
Length of output: 51
🏁 Script executed:
# Check if there are any other usages we might have missed
rg -n "saveRefsMap.current" src/utils/useEditableArray/index.tsx -B2 -A2Repository: ant-design/pro-components
Length of output: 902
saveRefsMap 的类型注解中包含不必要的 | null
Map 的类型定义为 React.RefObject<SaveEditableActionRef<any> | null>,但实际存储的值来自 createRef<SaveEditableActionRef<T>>(),始终返回有效的 RefObject,不会存储 null 值。
第 1158 行的 if (!saveRef?.current) 正确处理了 Map.get() 可能返回的 undefined(通过 || 操作符),但类型注解应移除 | null,改为:
Map<React.Key, React.RefObject<SaveEditableActionRef<any>>>
这样类型更精确,避免误导维护者认为会存储 null 值的 RefObject。
🤖 Prompt for AI Agents
In src/utils/useEditableArray/index.tsx around lines 1109 to 1111, the Map type
for saveRefsMap currently uses React.RefObject<SaveEditableActionRef<any> |
null> which incorrectly suggests stored RefObjects may be null; change the
generic to React.RefObject<SaveEditableActionRef<any>> so the Map is typed as
Map<React.Key, React.RefObject<SaveEditableActionRef<any>>> to reflect that
createRef always returns a non-null RefObject and keep the runtime check (if
(!saveRef?.current)) for undefined Map.get() results.
- Changed type references from `RefObject` to `React.Ref` for better compatibility in various components including forms, lists, and layouts. - Updated the `captchaRef` in the demo to use a specific type for improved type safety. - Enhanced documentation for `actionRef` across multiple components to reflect the updated type definitions. These changes improve type safety and maintainability across the codebase.
This reverts commit 9528f10.
|
冲突有点多了 |
- Bump versions for several packages including '@ant-design/cssinjs', '@rc-component/form', and '@umijs/utils'. - Remove outdated dependencies and add new ones such as '@rc-component/resize-observer' and '@rc-component/steps'. - Update peer dependencies for various components to ensure compatibility with the latest versions. - Clean up unnecessary entries and ensure consistency across the lock file.
|
改的都是类型,运行了下测试,发现很多都挂了 😂 |
- Updated multiple test files to replace the useRef hook with createRef for formRef, enhancing consistency in reference handling across ProForm and ModalForm tests. - This change improves clarity and aligns with best practices for creating refs in React.
React 开发版本升级到 v19.
resolutions是yarn特有的. 我们使用了pnpm似乎可以移除了.Summary by CodeRabbit
发布说明
升级依赖
重构
文档
✏️ Tip: You can customize this high-level summary in your review settings.