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

如何在React组件中指定forwardRef为可选?附TS报错解决

图标组件与按钮集成的TypeScript问题及实践探讨

我定义了一个导出多个图标的图标库,每个图标都会将ref转发给内部的svg,示例代码如下:

type ReactIconProps = SVGProps<SVGSVGElement>;

function CheckIconInner(props: ReactIconProps, ref: Ref<SVGSVGElement>) {
  return (
    <svg
      fill="none"
      height="1em"
      ref={ref}
      stroke="currentColor"
      viewBox="0 0 24 24"
      width="1em"
      xmlns="http://www.w3.org/2000/svg"
      {...props}
    >
      <path
        d="M20 6L9 17L4 12"
        strokeLinecap="round"
        strokeLinejoin="round"
        strokeWidth={2}
      />
    </svg>
  );
}
const CheckIcon = forwardRef(CheckIconInner);

我希望将这些图标作为按钮组件的props传入,让按钮可以包含图标,代码如下:

type ReactButtonProps = ButtonHTMLAttributes<HTMLButtonElement>;

export interface ButtonProps extends ReactButtonProps {
  Icon?: ComponentType<ReactIconProps>;
}

function Button({ children, Icon, ...props }: ButtonProps) {
  return (
    <button {...props}>
      {Icon !== undefined && <Icon />}
      {children}
    </button>
  );
}

export default function App() {
  return <Button Icon={CheckIcon}>OK</Button>;
}

但当将图标传入按钮(<Button Icon={CheckIcon}>OK</Button>)时,出现了TypeScript错误:

TS2322: Type 'ForwardRefExoticComponent<Omit<ReactIconProps, "ref"> & RefAttributes<SVGSVGElement>>' is not assignable to type 'ComponentType<ReactIconProps> | undefined'.
  Type 'ForwardRefExoticComponent<Omit<ReactIconProps, "ref"> & RefAttributes<SVGSVGElement>>' is not assignable to type 'FunctionComponent<ReactIconProps>'.
    Types of parameters 'props' and 'props' are incompatible.
      Type 'ReactIconProps' is not assignable to type 'Omit<ReactIconProps, "ref"> & RefAttributes<SVGSVGElement>'.
        Type 'SVGProps<SVGSVGElement>' is not assignable to type 'RefAttributes<SVGSVGElement>'.
          Types of property 'ref' are incompatible.
            Type 'LegacyRef<SVGSVGElement> | undefined' is not assignable to type 'Ref<SVGSVGElement> | undefined'.
              Type 'string' is not assignable to type 'Ref<SVGSVGElement> | undefined'.

问题核心在于图标组件需要处理ref,但当前的类型定义存在冲突。


1. 如何指定传入图标组件的ref为可选?

可以通过调整ButtonProps中Icon的类型定义,适配转发ref后的组件类型,有两种常用方案:

方案一:使用PropsWithoutRef简化类型

import { ComponentType, PropsWithoutRef } from 'react';

export interface ButtonProps extends ReactButtonProps {
  Icon?: ComponentType<PropsWithoutRef<ReactIconProps>>;
}

PropsWithoutRef<ReactIconProps>会自动排除ref属性,让TypeScript允许不传递ref的情况下使用图标组件,完美兼容转发ref的组件类型。

方案二:明确指定ForwardRef类型

export interface ButtonProps extends ReactButtonProps {
  Icon?: React.ForwardRefExoticComponent<
    React.PropsWithoutRef<ReactIconProps> & React.RefAttributes<SVGSVGElement>
  >;
}

这种方式更精准地匹配转发ref后的图标组件类型,适合需要明确类型细节的场景。

另外,也可以在图标组件内部将ref参数设为可选:

function CheckIconInner(props: ReactIconProps, ref?: Ref<SVGSVGElement>) {
  // 原有代码保持不变
}
const CheckIcon = forwardRef(CheckIconInner);

不过更推荐调整按钮的Icon属性类型,这样能更好地兼容所有转发ref的图标组件。


2. 在图标组件中使用ref是否是良好实践?

在图标组件中使用ref是合理且具有实际价值的实践,存在不少适用场景:

  • 无障碍访问优化:通过ref获取SVG元素,可设置焦点状态、添加屏幕阅读器相关属性,提升组件的无障碍性;
  • 动画效果控制:需要直接操作SVG元素实现复杂动画时,ref能提供直接访问DOM节点的途径;
  • 动态尺寸测量:在需要实时获取图标宽高信息时,ref可以帮助获取真实的DOM尺寸;
  • 第三方工具集成:某些第三方库可能需要直接访问SVG元素,转发ref能满足这类集成需求。

虽然通常建议优先通过props和状态管理控制组件、避免过度依赖ref,但在图标这类封装原生SVG的组件中,转发ref属于合理的使用场景,主流组件库的广泛应用也印证了这一点。


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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.03 06:40:37