React ref 类型错误:"MutableRefObject"不能赋值给"LegacyRef"
常见错误场景
在 React 中使用 useRef
时,如果类型声明不当,你可能会遇到此类类型错误:Type 'MutableRefObject<HTMLInputElement | undefined>' is not assignable to type 'LegacyRef<HTMLInputElement>'
问题现象
在 TypeScript 中声明 React ref 时,会遇到以下类型错误:
ts
Type 'MutableRefObject<HTMLInputElement | undefined>' is not assignable
to type 'LegacyRef<HTMLInputElement> | undefined'.
Type 'MutableRefObject<HTMLInputElement | undefined>' is not assignable
to type 'RefObject<HTMLInputElement>'.
Types of property 'current' are incompatible.
Type 'HTMLInputElement | undefined' is not assignable to type 'HTMLInputElement | null'.
Type 'undefined' is not assignable to type 'HTMLInputElement | null'.ts(2322)
此错误通常出现在以下两种场景中:
- 使用
React.useRef<T>()
未提供初始值 - Ref 对象没有正确处理
undefined
和null
的类型兼容性
错误示例代码
tsx
const InputElement = React.forwardRef((props:any, ref) => {
// ❌ 错误声明:未提供初始值且包含 undefined
const handleRef = React.useRef<HTMLInputElement | undefined>()
React.useImperativeHandle(ref, () => ({
setChecked(checked:boolean) {
if (handleRef.current) {
handleRef.current.checked = checked;
}
}
}), []);
return (
<input ref={handleRef} type="checkbox" /> {/* 错误位置 */}
)
})
错误原因分析
类型不兼容的根源
关键问题在 useRef
返回值中 current
属性的类型定义:
- 当不提供初始值时:
current
类型为T | undefined
- React 的 ref 属性期望:
current
类型为T | null
undefined
与null
在 TypeScript 中不兼容
解决方案
方法 1:提供 null
初始值(推荐)
tsx
const InputElement = React.forwardRef((props:any, ref) => {
// ✅ 正确:提供 null 初始值并移除 undefined
const handleRef = React.useRef<HTMLInputElement>(null)
React.useImperativeHandle(ref, () => ({
setChecked(checked:boolean) {
// 安全的 null 检查
if (handleRef.current) {
handleRef.current.checked = checked;
}
}
}), []);
return (
<input ref={handleRef} type="checkbox" /> {/* ✅ 类型正确 */}
)
})
方法 2:调整类型包含 null
(适合第三方库)
需要同时修改 useRef
和自定义 hook 的类型声明:
tsx
// ✅ 声明为 HTMLInputElement | null
const notificationsTrayRef = useRef<HTMLInputElement | null>(null)
// ✅ 在自定义 hook 中正确使用
useOnClickOutside(notificationsTrayRef, () => setShowDropdown(false))
ts
export function useOnClickOutside(
// ✅ 同时接受 undefined 和 null
ref: React.MutableRefObject<HTMLElement | undefined | null>,
handler: () => void
): void {
// hook 实现...
}
最佳实践
- 始终提供初始值:使用
useRef<T>(null)
而不是useRef<T>()
- 类型安全检查:访问
ref.current
前进行空值检查tsif (myRef.current) { // TypeScript 会自动推断 current 非空 myRef.current.focus() }
- Forwarding refs 时:使用
React.forwardRef
并正确定义类型tsxconst MyComponent = React.forwardRef<HTMLInputElement>((props, ref) => { return <input ref={ref} {...props} />; });
为什么 React 使用 null
?
React 在组件卸载时会自动将 ref 的 current
设置为 null
,这是标准设计模式。使用 null
而非 undefined
可保证类型系统与实际行为一致。