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

React中forwardRef类型问题:HOC转发Ref时TypeScript报错

问题描述

我实现了一个用于条件渲染的HOC(高阶组件),初始代码运行正常。现在想要通过React的forwardRef函数将接收到的Ref转发给原始组件,修改后TypeScript抛出类型不兼容错误,请问代码存在什么问题、forwardRef的返回类型是什么以及如何正确使用?

初始代码

import React, { FC } from 'react';

interface ConditionalProps {
  renderIf?: boolean
}
const ConditionalizeComponent = <Props,>(
  OriginalComponent: FC<Props>
): FC<Props & ConditionalProps> => {
  return (props) => {
    return <OriginalComponent {...props} />
  }
}

interface TestComponentProps
  extends React.DetailedHTMLProps<
    React.HTMLAttributes<HTMLParagraphElement>,
    HTMLParagraphElement
  > {}

function BBB() {
  const TestComponent: FC<TestComponentProps> = ConditionalizeComponent(
    (props) => <p {...props} />
  )
  return <TestComponent renderIf={true} />
}

修改后代码

import React, { FC, forwardRef } from 'react';

interface ConditionalProps {
  renderIf?: boolean
}
const ConditionalizeComponent = <Props,>(
  OriginalComponent: FC<Props>
): FC<Props & ConditionalProps> => {
  return forwardRef((props, ref) => {
    return <OriginalComponent {...props} ref={ref} />
  })
}

interface TestComponentProps
  extends React.DetailedHTMLProps<
    React.HTMLAttributes<HTMLParagraphElement>,
    HTMLParagraphElement
  > {}

function BBB() {
  const TestComponent: FC<TestComponentProps> = ConditionalizeComponent(
    (props) => <p {...props} />
  )
  return <TestComponent renderIf={true} />
}

TypeScript错误信息

Type 'ForwardRefExoticComponent<PropsWithoutRef<Props & ConditionalProps> & RefAttributes<unknown>>' is not assignable to type 'FC<Props & ConditionalProps>'.
  Types of property 'propTypes' are incompatible.
    Type 'WeakValidationMap<PropsWithoutRef<Props & ConditionalProps> & RefAttributes<unknown>> | undefined' is not assignable to type 'WeakValidationMap<Props & ConditionalProps> | undefined'.
      Type 'WeakValidationMap<PropsWithoutRef<Props & ConditionalProps> & RefAttributes<unknown>>' is not assignable to type 'WeakValidationMap<Props & ConditionalProps>'.
        Type 'keyof Props | "renderIf"' is not assignable to type 'keyof PropsWithoutRef<Props & ConditionalProps> | keyof RefAttributes<unknown>'.
          Type 'keyof Props' is not assignable to type 'keyof PropsWithoutRef<Props & ConditionalProps> | keyof RefAttributes<unknown>'.
            Type 'string | number | symbol' is not assignable to type 'keyof PropsWithoutRef<Props & ConditionalProps> | keyof RefAttributes<unknown>'.
              Type 'string' is not assignable to type 'keyof PropsWithoutRef<Props & ConditionalProps> | keyof RefAttributes<unknown>'.ts(2322)

问题分析与解决方案

1. 代码核心问题

  • 类型不匹配:你把HOC的返回类型声明为FC<Props & ConditionalProps>,但forwardRef返回的是ForwardRefExoticComponent类型,二者不兼容。FC(函数组件类型)不包含ref相关属性定义,而forwardRef生成的组件会自动处理ref属性,内部使用PropsWithoutRef剔除了props中的ref字段,同时添加了合法的ref属性类型。
  • 原始组件类型约束缺失:传入HOC的OriginalComponent被定义为FC<Props>,但普通FC不接收ref参数,直接给它传ref会触发类型错误——只有类组件、被forwardRef包裹的函数组件、原生DOM组件这类支持ref的组件,才能接收ref属性。

2. forwardRef的返回类型

forwardRef的返回类型是ForwardRefExoticComponent<PropsWithoutRef<P> & RefAttributes<T>>,其中:

  • P:组件的props类型
  • T:转发的ref的目标类型(比如DOM元素、类组件实例)
  • PropsWithoutRef<P>:自动剔除P中的ref属性,避免重复传递
  • RefAttributes<T>:添加合法的ref属性类型,支持RefObject<T>、回调ref或null

3. 正确实现代码

调整HOC的泛型参数和返回类型,同时约束原始组件支持ref:

import React, { forwardRef, PropsWithoutRef, RefAttributes, ForwardRefRenderFunction } from 'react';

interface ConditionalProps {
  renderIf?: boolean;
}

// 泛型新增ref类型T,约束原始组件为支持ref的函数组件
const ConditionalizeComponent = <Props, T = unknown>(
  OriginalComponent: ForwardRefRenderFunction<T, Props>
): React.ForwardRefExoticComponent<PropsWithoutRef<Props & ConditionalProps> & RefAttributes<T>> => {
  return forwardRef((props, ref) => {
    // 解构出条件渲染参数,剩余props传给原始组件
    const { renderIf, ...restProps } = props;
    // 条件渲染逻辑:renderIf为true时渲染,否则返回null
    if (!renderIf) return null;
    return <OriginalComponent {...restProps} ref={ref} />;
  });
};

interface TestComponentProps
  extends React.DetailedHTMLProps<
    React.HTMLAttributes<HTMLParagraphElement>,
    HTMLParagraphElement
  > {}

function BBB() {
  // 定义支持ref的TestComponent,指定ref类型为HTMLParagraphElement
  const TestComponent = ConditionalizeComponent<TestComponentProps, HTMLParagraphElement>(
    (props, ref) => <p {...props} ref={ref} />
  );
  
  // 使用useRef获取DOM元素引用
  const pRef = React.useRef<HTMLParagraphElement>(null);
  
  return <TestComponent renderIf={true} ref={pRef}>测试文本</TestComponent>;
}

关键修正点

  • 泛型新增T参数,用于指定转发的ref类型,原始组件使用ForwardRefRenderFunction<T, Props>确保能接收ref参数
  • HOC返回类型匹配forwardRef的实际返回类型,不再使用FC
  • 补充了条件渲染的核心逻辑(符合HOC的命名意图)
  • 使用时可以明确指定泛型参数,让TypeScript正确推导类型,同时正常传递ref

内容的提问来源于stack exchange,提问作者Islam Y-

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.29 04:44:56