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

如何为条件式Anchor/Button组件正确定义React.forwardRef类型?

React 条件组件的 Ref 类型适配方案

你的问题核心是联合类型的 Props 和 forwardRef 的泛型无法自动匹配对应关系:当as为"a"时,React期望接收Ref<HTMLAnchorElement>类型,但你传递的是ForwardedRef<HTMLAnchorElement | HTMLButtonElement>,TypeScript无法确认当前场景下ref的具体类型,因此报错。

最优解决方案:泛型关联组件类型与 Ref

通过泛型将as属性的值和对应的元素类型绑定,让TypeScript能精准推断ref的类型:

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

type TriggerElementType = "button" | "a";

// 基于as的值,推导对应的Props类型
type TriggerProps<T extends TriggerElementType> = 
  T extends "button" 
    ? React.ButtonHTMLAttributes<HTMLButtonElement> & { as: T }
    : React.AnchorHTMLAttributes<HTMLAnchorElement> & { as: T };

// 泛型组件,关联as类型与ref类型
const Trigger = forwardRef(function <T extends TriggerElementType>(
  { as, ...props }: TriggerProps<T>, 
  ref: React.ForwardedRef<
    T extends "button" ? HTMLButtonElement : HTMLAnchorElement
  >
) {
  return createElement(as, {
    ...props,
    ref
  } as React.HTMLAttributes<
    T extends "button" ? HTMLButtonElement : HTMLAnchorElement
  >);
});

简化方案:类型断言(不推荐,牺牲类型安全)

如果不想重构为泛型组件,也可以通过类型断言绕过TypeScript的检查,但会失去部分类型安全性:

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

type ConditionalProps =
  | ({
      as: "button";
    } & React.ButtonHTMLAttributes<HTMLButtonElement>)
  | ({
      as: "a";
    } & React.AnchorHTMLAttributes<HTMLAnchorElement>);

const Trigger = forwardRef<
  HTMLAnchorElement | HTMLButtonElement,
  ConditionalProps
>(function Trigger({ as, ...props }, ref) {
  return createElement(as, {
    ...props,
    // 断言ref为当前元素支持的类型
    ref: ref as React.Ref<HTMLAnchorElement | HTMLButtonElement>
  });
});

为什么泛型方案更好?

泛型方案能让TypeScript在编译阶段就确保as的类型、Props的属性、ref的类型完全匹配,避免后续使用组件时出现类型错误;而类型断言只是强制关闭了TypeScript的检查,无法保证类型一致性。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.28 23:15:03