You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

React TypeScript封装函数返回组件时如何保留泛型类型

问题背景
  • 现有封装的Suspense包裹工具函数实现如下:
import { ComponentType, Suspense } from 'react';

function useSuspense<P extends Record<string, any>>(WrappedComponent: ComponentType<P>) {

  function Component(props: P) {
    return (
      <Suspense fallback={fallback}>
        <WrappedComponent {...props} />
      </Suspense>
    );
  }

  return Component;
}
  • 常规使用场景是包裹React.lazy懒加载的组件:
const MyCompo = useMemo(() => lazy(() => import('path-to-component')), []);
  • 存在的问题:当传入带多泛型参数的泛型组件时,封装后返回的组件会丢失原有的泛型能力。例如传入如下定义的组件:
type SomeTypeDefinition<T, R> = {
  typeValue?: T;
  returnValue?: R;
  someValue?: string;
};

export default function DummyComponent<T, R>(props: SomeTypeDefinition<T, R>) {
  return (
    <p>
      DummyComponent <b>{props.someValue}</b>
    </p>
  );
}

原生DummyComponent支持<DummyComponent<TType, RType> />的显式泛型传参用法,类型提示可以正常识别typeValue、returnValue的自定义类型;但经过useSuspense封装后,返回的组件无法支持该用法,泛型参数会被固化为宽泛类型。

问题根因

原实现的泛型参数P是在调用useSuspense的时刻就被静态推导为固定类型,内部返回的Component是props类型固定为P的非泛型组件,相当于把原泛型组件的动态泛型入参能力直接抹除了,无法在组件使用阶段再传入自定义泛型参数。

改造方案

核心思路是不在封装层固化组件的props类型,直接透传原组件的完整类型签名,改造后的实现如下:

import { ComponentType, Suspense, ReactNode } from 'react';

function useSuspense<T extends ComponentType<any>>(
  WrappedComponent: T,
  fallback: ReactNode = null
) {
  const Component = (props: React.ComponentProps<T>) => {
    return (
      <Suspense fallback={fallback}>
        <WrappedComponent {...props} />
      </Suspense>
    );
  };

  // 保留原组件的调试标识
  Component.displayName = `WithSuspense(${
    WrappedComponent.displayName || WrappedComponent.name || 'Component'
  })`;

  // 直接断言为原组件类型,完整保留泛型签名
  return Component as T;
}

关键改动说明

  • 泛型参数直接接收整个组件类型T,而非提前固定props类型P,从源头避免泛型被静态固化
  • 内部组件的props类型通过React.ComponentProps<T>自动推导,兼容普通组件、泛型组件的所有props场景
  • 返回值直接断言为原组件类型T,原组件上的泛型参数、静态属性、ref支持等类型信息会被完整保留
  • 补充了原实现缺失的fallback入参,支持自定义加载态

使用示例

包裹普通泛型组件:

const SuspenseDummy = useSuspense(DummyComponent, <div>加载中...</div>);

// 使用时完全保留泛型传参能力
function Demo() {
  return (
    <SuspenseDummy<number, string>
      typeValue={123} // 自动推导为number类型,类型提示正常
      returnValue="test" // 自动推导为string类型,类型提示正常
      someValue="示例"
    />
  );
}

包裹lazy懒加载的泛型组件时,需要给lazy结果补充类型断言,避免导入阶段丢失泛型签名:

const LazyDummy = lazy(() =>
  import('./DummyComponent').then(mod => ({ default: mod.default }))
) as typeof DummyComponent; // 断言为原组件的完整泛型类型

const LazySuspenseDummy = useSuspense(LazyDummy, <div>组件加载中</div>);

如果需要支持ref透传,可以在这个实现基础上配合forwardRef做同逻辑的类型透传,核心原则就是不要在封装层把泛型组件的类型固化成固定props的普通组件。

内容的提问来源于stack exchange,提问作者danyhiol

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.28 04:48:15