TypeScript结合MUI使用useImperativeHandle类型报错如何解决
错误原因
这个错误不是缺失类型定义导致的,核心问题是React.forwardRef的泛型声明和实际通过useImperativeHandle暴露的ref值类型不匹配:
- 你在调用
React.forwardRef时传入的第一个泛型参数是HTMLDivElement,TypeScript会据此判定你要向父组件直接暴露原生div DOM实例作为ref的返回值 - 但你实际在
useImperativeHandle中返回的是一个带boxgetter的自定义对象{ readonly box: HTMLDivElement | null },和声明的HTMLDivElement类型完全不符,因此TS会提示返回的对象缺少HTMLDivElement上的align、addEventListener等数百个原生属性。
另外原代码还有一处类型问题:forwardRef的第二个泛型参数对应组件接收的props类型,你传入了空对象{},会导致组件使用时所有Box自带的props丢失类型校验。
修复方案
根据你的实际封装需求二选一即可:
方案1:需要自定义ref暴露结构(父组件通过.box访问内部DOM)
如果你确实想封装一层,让父组件拿到的ref实例通过box属性访问内部Box对应的DOM节点,只需要修正forwardRef的泛型参数即可:第一个泛型传入你实际暴露的自定义ref类型,第二个泛型传入组件的props类型BoxProps,不需要额外引入PropsWithChildren,BoxProps本身已经包含children类型定义。
修复后代码:
import React, { useRef, useImperativeHandle } from 'react'; import Box, { BoxProps } from '@mui/material/Box'; // 定义暴露给父组件的ref类型 type Bx2Handle = { box: HTMLDivElement | null; }; const Bx2 = React.forwardRef<Bx2Handle, BoxProps>( function BoxRef(props, ref) { const boxRef = useRef<HTMLDivElement>(null); useImperativeHandle(ref, () => ({ get box() { return boxRef.current; } }), [boxRef]); return <Box {...props} component="div" ref={boxRef} />; } );
父组件使用时可以获得完整的类型提示:
import { useRef, useEffect } from 'react'; const Demo = () => { const bx2Ref = useRef<Bx2Handle>(null); useEffect(() => { // TS会自动提示box属性,类型为HTMLDivElement | null console.log(bx2Ref.current?.box?.getBoundingClientRect()); }, []); return <Bx2 ref={bx2Ref} sx={{ p: 2 }}>测试内容</Bx2>; };
方案2:仅需要透传内部Box的ref给父组件
如果你不需要自定义ref结构,只是想把内部Box对应的DOM实例直接暴露给父组件,完全不需要额外声明内部ref和调用useImperativeHandle,直接将forwardRef接收的ref挂载到Box组件上即可,代码更简洁:
import React from 'react'; import Box, { BoxProps } from '@mui/material/Box'; const Bx2 = React.forwardRef<HTMLDivElement, BoxProps>( function BoxRef(props, ref) { return <Box {...props} component="div" ref={ref} />; } );
父组件使用时拿到的ref直接就是原生div实例:
import { useRef, useEffect } from 'react'; const Demo = () => { const bx2Ref = useRef<HTMLDivElement>(null); useEffect(() => { // 直接获取DOM实例调用原生方法 console.log(bx2Ref.current?.getBoundingClientRect()); }, []); return <Bx2 ref={bx2Ref} sx={{ p: 2 }}>测试内容</Bx2>; };
内容的提问来源于stack exchange,提问作者mjwrazor
相关产品推荐
相关产品推荐

