TypeScript下结合React.lazy、Suspense与forwardRef编写异步HOC的类型报错处理
类型报错根因与修复方案
核心错误来源
原代码的类型报错完全来自两处对React API泛型规则的错位使用:
- 记错了
forwardRef的泛型参数顺序:这个API的泛型签名是forwardRef<RefInstanceType, PropsType>,第一个参数对应ref指向的组件实例类型,第二个参数才是组件接收的props类型。原代码把props类型P直接传给了第一个泛型位,导致ref类型和props类型完全错位,触发全链路类型不匹配报错。 - 泛型缺少基础约束:props相关的泛型没有加
extends object的限制,和React内部组件的props类型校验规则不兼容,进一步放大了类型推导的错误。
具体修复调整
- 给所有props相关的泛型加上
extends object约束,完全匹配React组件的props类型要求 - 修正
forwardRef的泛型传参顺序,把props类型放到第二个泛型位,第一个泛型位默认用unknown承接ref类型,需要强类型时TS会自动从加载的组件类型里推导 - 去掉硬编码的
ComponentType<P>返回值标注,让TS根据forwardRef的返回值自动生成正确的、带ref转发能力的组件类型,避免返回值类型不匹配
修复后可直接运行的代码
import { ComponentType, forwardRef, Ref, lazy, Suspense } from 'react'; type AsyncComponentFactory<P extends object> = () => Promise<{ default: ComponentType<P> }>; export function loadable<P extends object, RefType = unknown>(factory: AsyncComponentFactory<P>) { const LazyComponent = lazy(factory); const WrappedWithLoadableComponent = forwardRef<RefType, P>((props, ref) => ( <Suspense fallback="loading"> <LazyComponent {...props} ref={ref} /> </Suspense> )); WrappedWithLoadableComponent.displayName = 'LazyExoticComponent(loadableComponent)'; return WrappedWithLoadableComponent; } const JobsView = loadable(() => import(/* webpackChunkName: "JobsView" */ '@views/Jobs'));
修复后的实际表现
- 传入组件的props会完整透传给异步加载的目标组件,类型完全匹配,不会触发类型校验报错
- 外层包裹的组件可以正常通过
ref获取到内部异步组件的实例,ref的类型会根据内部组件的定义自动推导,不需要额外写类型断言
内容的提问来源于stack exchange,提问作者SWAN ZHANG
相关产品推荐
相关产品推荐

