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

React+TypeScript多态组件:通过Props选择元素类型

如何让TextBody组件根据span属性动态继承对应元素的HTML属性

当然可以实现,我们可以通过TypeScript的区分联合类型或函数重载来达成需求,让组件在不同span取值下仅接收对应元素的合法HTML属性,避免联合类型带来的属性冗余问题。

方案1:使用区分联合类型

先定义两种分别对应span为true和false的Props接口,再通过联合类型组合,以span作为区分符:

import { HTMLAttributes } from "react";
import classNames from "classnames";

// 定义两种状态的Props类型
type TextBodyProps = 
  | ({ span: true } & HTMLAttributes<HTMLSpanElement> & { type: "s" | "m" })
  | ({ span?: false } & HTMLAttributes<HTMLParagraphElement> & { type: "s" | "m" });

export const TextBody = ({
  span,
  type,
  className,
  children,
  ...props
}: TextBodyProps) => {
  const textBodyClassNames = {
    s: "text-body-s font-light leading-relaxed max-w-sm",
    m: "text-body-m font-light leading-relaxed max-w-sm",
  };

  const TextBodyElement = span ? "span" : "p";

  return (
    <TextBodyElement
      {...props as any}
      className={classNames(textBodyClassNames[type], className)}
    >
      {children}
    </TextBodyElement>
  );
};

当传入span={true}时,组件仅接收HTMLSpanElement的属性;不传或传入span={false}时,仅接收HTMLParagraphElement的属性。

方案2:使用函数重载 + 泛型

如果想避免类型断言,可以用函数重载明确不同参数下的属性校验规则:

import { HTMLAttributes } from "react";
import classNames from "classnames";

// 重载签名1:span为true时,匹配span元素的属性
function TextBody(props: { span: true, type: "s" | "m" } & HTMLAttributes<HTMLSpanElement>): JSX.Element;
// 重载签名2:span为false或不传时,匹配p元素的属性
function TextBody(props: { span?: false, type: "s" | "m" } & HTMLAttributes<HTMLParagraphElement>): JSX.Element;
// 实现签名
function TextBody({
  span,
  type,
  className,
  children,
  ...props
}: { span?: boolean, type: "s" | "m" } & (HTMLAttributes<HTMLSpanElement> | HTMLAttributes<HTMLParagraphElement>)) {
  const textBodyClassNames = {
    s: "text-body-s font-light leading-relaxed max-w-sm",
    m: "text-body-m font-light leading-relaxed max-w-sm",
  };

  const TextBodyElement = span ? "span" : "p";

  return (
    <TextBodyElement
      {...props}
      className={classNames(textBodyClassNames[type], className)}
    >
      {children}
    </TextBodyElement>
  );
}

export { TextBody };

这种方式下,TypeScript会根据你传入的span值自动校验属性合法性,无需额外类型断言。

效果验证

// 合法,p元素支持align属性
<TextBody type="m" align="center">段落文本</TextBody>

// 报错,span元素无align属性
<TextBody type="s" span={true} align="center">行内文本</TextBody>

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.11 15:45:41