React TypeScript Portal示例代码报错,如何修复?
修复React Portal组件的TypeScript与ESLint冲突问题
问题情况
使用严格模式的TypeScript和ESLint开发React Portal组件时,遇到两类冲突错误:
- 清理函数中使用
void触发ESLint提示:Expected 'undefined' and instead saw 'void' - 移除
void后,TypeScript抛出类型不兼容错误,提示清理函数返回值不符合EffectCallback要求,具体错误信息:
Argument of type '() => () => HTMLDivElement' is not assignable to parameter of type 'EffectCallback'. Type '() => HTMLDivElement' is not assignable to type 'void | Destructor'. Type '() => HTMLDivElement' is not assignable to type 'Destructor'. Type 'HTMLDivElement' is not assignable to type 'void | { [UNDEFINED_VOID_ONLY]: never; }'
错误根源
portalRoot?.removeChild(current)执行后会返回被移除的DOM元素(HTMLDivElement类型),但React的Effect清理函数要求返回void或符合Destructor的类型(本质要求无有效返回值)。直接使用void会触发ESLint的no-void规则,移除void则会因返回DOM元素导致TypeScript类型检查失败。
修复方案
方案一:用大括号包裹清理逻辑(推荐)
将清理函数改为带大括号的函数体,不显式返回任何值,默认返回undefined,同时满足ESLint和TypeScript要求:
import React, { useEffect, useRef, ReactNode } from 'react' import { createPortal } from 'react-dom' interface Props { children?: ReactNode portalId: string } export const Portal: React.FC<Props> = ({ children, portalId }) => { const el = useRef(document.createElement('div')) useEffect(() => { const portalRoot = document.querySelector(`#${portalId}`) as HTMLElement const current = el.current portalRoot.appendChild(current) // 用大括号包裹,执行移除操作但不返回值 return () => { portalRoot?.removeChild(current) } }, [portalId]) return createPortal(children, el.current) }
方案二:逗号表达式强制返回undefined
如果偏好单行写法,可使用逗号表达式,执行移除操作后返回undefined:
// 替换清理函数行 return () => (portalRoot?.removeChild(current), undefined)
说明
两种方案均能确保清理函数返回undefined,既通过TypeScript的严格类型检查,又避免ESLint对void的报错。若项目中no-void规则并非强制,也可通过调整ESLint配置允许void的使用,但上述方案更符合严格模式下的代码规范。
内容的提问来源于stack exchange,提问作者Salvatore Argentieri
相关产品推荐
相关产品推荐

