fix(type): restrict useWatch typed paths#799
Conversation
|
@biubiukam is attempting to deploy a commit to the React Component Team on Vercel. A member of the Team first needs to authorize it. |
|
No actionable comments were generated in the recent review. 🎉 ℹ️ Recent review info⚙️ Run configurationConfiguration used: Organization UI Review profile: CHILL Plan: Pro Run ID: 📒 Files selected for processing (3)
Walkthrough在 ChangesuseWatch 依赖项类型约束
Estimated code review effort🎯 2 (Simple) | ⏱️ ~10 minutes Suggested reviewers
Poem
🚥 Pre-merge checks | ✅ 4 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (4 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing Touches🧪 Generate unit tests (beta)
Warning There were issues while running some tools. Please review the errors and either fix the tool's configuration or disable the tool if it's a critical failure. 🔧 ESLint
docs/examples/useWatch.tsxESLint skipped: missing config or dependency (missing-dependency). The ESLint configuration references a package that is not available in the sandbox. src/hooks/useWatch.tsESLint skipped: the ESLint configuration for this file references a package that is not available in the sandbox. tests/useWatch.test.tsxESLint skipped: the ESLint configuration for this file references a package that is not available in the sandbox. 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 |
There was a problem hiding this comment.
Code Review
This pull request introduces stricter type checking for the useWatch hook by defining IsAny and AnyNamePath helper types to validate dependencies against the form's generic type. It also updates the test suite and documentation to reflect these changes. The review feedback highlights a critical limitation: restricting the fallback overload to never prevents compilation for paths of length 5 or more, even when explicit type overrides are provided. To resolve this, the reviewer suggests refactoring AnyNamePath and the useWatch overloads to accept and respect an explicit ValueType parameter, allowing users to bypass the strictness for deep or dynamic paths.
Important
The consumer version of Gemini Code Assist on GitHub is being sunset. Starting June 18, 2026, new organization installations will be blocked, and all code review activity will officially cease on July 17, 2026.
For more details on the timeline and next steps, please review the Help Documentation.
| type IsAny<T> = 0 extends 1 & T ? true : false; | ||
| type AnyNamePath<TForm extends FormInstance> = | ||
| IsAny<GetGeneric<TForm>> extends true ? NamePath : never; |
There was a problem hiding this comment.
By restricting the fallback overload to never for all typed forms, any path of length 5 or more (which exceeds the explicit 4-level overloads) will fail to compile, even if the path is valid or if the user explicitly overrides the type parameter (e.g., useWatch<string>(['a', 'b', 'c', 'd', 'e'], form)).
We can make AnyNamePath more flexible by allowing it to accept NamePath if the user explicitly provides a custom ValueType (other than Store) or any. This preserves strict type checking for the default case while allowing explicit type overrides for deep or dynamic paths.
type IsAny<T> = 0 extends 1 & T ? true : false;
type AnyNamePath<TForm extends FormInstance, ValueType = Store> =
IsAny<GetGeneric<TForm>> extends true
? NamePath
: IsAny<ValueType> extends true
? NamePath
: Store extends ValueType
? never
: NamePath;| function useWatch<ValueType = Store, TForm extends FormInstance = FormInstance>( | ||
| dependencies: AnyNamePath<TForm>, | ||
| form?: TForm | WatchOptions<TForm>, | ||
| ): ValueType; |
There was a problem hiding this comment.
Pass the ValueType generic parameter to AnyNamePath so that explicit type overrides (e.g., useWatch<string>(...)) can bypass the never restriction for deep or dynamic paths.
| function useWatch<ValueType = Store, TForm extends FormInstance = FormInstance>( | |
| dependencies: AnyNamePath<TForm>, | |
| form?: TForm | WatchOptions<TForm>, | |
| ): ValueType; | |
| function useWatch<ValueType = Store, TForm extends FormInstance = FormInstance>( | |
| dependencies: AnyNamePath<TForm, ValueType>, | |
| form?: TForm | WatchOptions<TForm>, | |
| ): ValueType; |
|
@afc163 这个 PR 是否需要调整呢 |
🤔 This is a ...
🔗 Related Issues
Related to ant-design/ant-design#58405.
close ant-design/ant-design#58405
💡 Background and Solution
Form.useWatchcan infer watched value types from typed form instances, but the broadNamePathfallback overload also accepts unknown field paths when a typed form is provided. As a result, invalid field names can pass TypeScript checks.This change keeps the runtime behavior unchanged and preserves the existing loose behavior for untyped forms. For typed form instances, the broad fallback overload is now limited to forms whose value type is
any, so valid field paths continue to infer their value types while unknown top-level or nested paths are rejected by TypeScript.The type coverage now verifies that:
undefinedin the return type;📝 Change Log
Form.useWatchtype checking for typed forms to reject unknown field paths.Form.useWatch在类型化表单中的类型检查,未知字段路径将被拒绝。✅ Test Plan
npm run lint:tscnpm test -- tests/useWatch.test.tsx --runInBandnpm run lintnpm test -- --runInBandnpm run compileSummary by CodeRabbit
发布说明
功能改进
测试
文档