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

如何用TypeScript正确实现根据isDiv属性渲染HTML元素并透传属性的React组件?

问题根因

你的实现出现类型报错,本质是两个设计上的问题导致的:

  • 入参结构和使用习惯脱节:原JS版本将元素透传属性封装为单独的elementProps对象入参,但你给出的使用示例是直接将HTML属性作为组件顶层props传入(比如<Button type="submit" />),两种写法完全不匹配,实际使用时需要手动套一层属性对象,体验很差。
  • 判别联合的类型关联被解构破坏:原TS代码中isDiv和elementProps是props上两个独立的显式定义属性,当从联合类型的props中把这两个属性解构为独立变量时,TS会分别取两个属性的联合类型,无法通过isDiv的取值反向关联elementProps对应的具体类型。最终在往div或button上传递属性时,TS会认为传入的属性集合可能是另一个元素的属性类型,而两种元素的属性集合并不完全兼容,就抛出了Property 'align' is missing in type 'HTMLButtonElement' but required in type 'HTMLDivElement'的错误。
正确实现方案

我们只需要调整类型定义结构,让判别属性isDiv和对应元素的原生属性直接绑定为联合分支,不再单独设置elementProps入参,通过rest参数收集所有需要透传的属性,就能让TS的类型收窄正常工作。

1. 类型定义

import React from 'react';

// 渲染div的分支:必须显式传入isDiv={true}
type DivVariantProps = {
  isDiv: true;
} & React.HTMLAttributes<HTMLDivElement>;

// 渲染button的分支:isDiv可以不传,或显式传入isDiv={false}
type ButtonVariantProps = {
  isDiv?: false;
} & React.ButtonHTMLAttributes<HTMLButtonElement>;

// 组件整体的props类型,为两个分支的判别联合
type ButtonProps = DivVariantProps | ButtonVariantProps;

这里特意将DivVariantProps的isDiv设为必填的true,ButtonVariantProps的isDiv设为可选的false,避免undefined值带来的类型歧义,让TS能准确识别分支:

  • 未传isDiv或传isDiv={false}时,走button渲染分支
  • 显式传isDiv={true}时,走div渲染分支

2. 组件实现

const Button: React.FC<ButtonProps> = (props) => {
  // 单独取出判别属性isDiv和children,剩余属性全部收集为透传的restProps
  const { isDiv, children, ...restProps } = props;

  if (isDiv) {
    // 传入div时,TS会自动收窄restProps为合法的div属性类型
    // 合并类名,避免透传的className覆盖默认的button基础类
    return (
      <div
        {...restProps}
        className={`button ${restProps.className ?? ''}`.trim()}
      >
        {children}
      </div>
    )
  }

  // 传入button时,TS会自动收窄restProps为合法的button属性类型
  return (
    <button
      {...restProps}
      className={`button ${restProps.className ?? ''}`.trim()}
    >
      {children}
    </button>
  )
}
效果验证

实现完成后完全匹配预期的使用方式,同时TS会自动做属性合法性校验:

  • 不传isDiv时,渲染button元素,支持传入所有button原生属性:
    <Button type="submit" onClick={() => console.log('click')} />
    // 最终渲染:<button class="button" type="submit">...</button>
    
  • 传入isDiv时,渲染div元素,支持传入所有div原生属性,传入button专属的无效属性时TS会直接报错:
    <Button isDiv role="button" onClick={() => console.log('click')} />
    // 最终渲染:<div class="button" role="button">...</div>
    

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.10.26 20:54:04