Skip to content

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)

此错误通常出现在以下两种场景中:

  1. 使用 React.useRef<T>() 未提供初始值
  2. Ref 对象没有正确处理 undefinednull 的类型兼容性

错误示例代码

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
  • undefinednull 在 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 实现...
}

最佳实践

  1. 始终提供初始值:使用 useRef<T>(null) 而不是 useRef<T>()
  2. 类型安全检查:访问 ref.current 前进行空值检查
    ts
    if (myRef.current) {
      // TypeScript 会自动推断 current 非空
      myRef.current.focus()
    }
  3. Forwarding refs 时:使用 React.forwardRef 并正确定义类型
    tsx
    const MyComponent = React.forwardRef<HTMLInputElement>((props, ref) => {
      return <input ref={ref} {...props} />;
    });

为什么 React 使用 null

React 在组件卸载时会自动将 ref 的 current 设置为 null,这是标准设计模式。使用 null 而非 undefined 可保证类型系统与实际行为一致。