TypeScript如何实现支持重载的高阶组件(HOC)
重载式高阶组件(HOC)实现方案
核心实现思路
要实现同时支持两种调用方式的类型安全HOC,核心是两个部分:
- 编写独立的TS重载签名,分别对应两种调用场景,将泛型定义在重载签名上以支持自动类型推导
- 运行时通过判断二次调用的入参类型区分调用路径,不需要对首个入参做复杂校验,配合TS编译时检查即可保证类型安全
完整实现代码
import { ComponentType, JSX, createElement } from 'react'; // Fallback组件的props类型定义 interface FallbackProps { error: Error; resetErrorBoundary: (...args: Array<unknown>) => void; } // 内置默认错误兜底组件,单参数调用时默认使用 const DefaultFallback: ComponentType<FallbackProps> = ({ error }) => { return createElement('div', { role: 'alert' }, `加载失败:${error.message}`); } /** * 重载签名1:传入自定义错误兜底组件,返回柯里化的包装函数 * 对应调用方式:hoc(CustomFallback)(WrappedComponent) */ function withErrorBoundary<P extends object>( fallback: ComponentType<FallbackProps> ): (WrappedComponent: ComponentType<P>) => (props: P) => JSX.Element; /** * 重载签名2:直接传入被包裹的业务组件,返回包装后的最终组件 * 对应调用方式:hoc(WrappedComponent) */ function withErrorBoundary<P extends object>( WrappedComponent: ComponentType<P> ): (props: P) => JSX.Element; // 底层实现(该签名对外不可见) function withErrorBoundary(firstArg: ComponentType<any>) { // 通用组件包装逻辑,可替换为实际的错误边界实现 const wrapComponent = ( WrappedComponent: ComponentType<any>, Fallback: ComponentType<FallbackProps> ) => { const Wrapped = (props: any) => { // 示例为简化错误捕获逻辑,生产环境建议直接复用react-error-boundary的ErrorBoundary组件 try { return createElement(WrappedComponent, props); } catch (err) { return createElement(Fallback, { error: err as Error, resetErrorBoundary: () => {} }); } }; Wrapped.displayName = `withErrorBoundary(${WrappedComponent.displayName || WrappedComponent.name || 'Component'})`; return Wrapped; }; // 返回兼容两种场景的函数 return function hybridFn(secondArg: any) { // 判断入参是否为React组件(兼容函数组件、类组件、memo/forwardRef包装组件) const isComponent = typeof secondArg === 'function' || (typeof secondArg === 'object' && secondArg !== null && '$$typeof' in secondArg); if (isComponent) { // 柯里化调用路径:第一个参数是fallback,第二个参数是被包裹组件 return wrapComponent(secondArg, firstArg); } // 直接调用路径:第一个参数是被包裹组件,当前参数是组件props,直接渲染 return createElement(wrapComponent(firstArg, DefaultFallback), secondArg); }; }
类型校验效果
两种调用方式都可以获得完整的类型提示:
- 直接传入业务组件时,TS自动匹配第二个重载,返回的组件props类型和原业务组件完全一致
- 传入自定义fallback再柯里化传入业务组件时,TS自动匹配第一个重载,最终返回的组件props类型同样和业务组件保持一致,fallback组件的props会被校验为
FallbackProps类型,传错会直接报类型错误
注意事项
- 不要把泛型参数定义在最外层的HOC类型上,必须放在每个重载签名上,否则TS无法根据传入的组件自动推导props类型
- 实现签名的类型需要兼容所有重载的入参、返回值类型,该签名不会暴露给调用方
- 示例中的错误捕获逻辑是简化版本,生产使用建议直接引入成熟的错误边界库做内部包裹,不需要自行实现错误捕获逻辑
内容的提问来源于stack exchange,提问作者Mahmood Bagheri
相关产品推荐
相关产品推荐

