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

React函数组件中TypeScript的...rest属性声明问题

如何在React+TypeScript组件中正确声明...rest属性

问题场景

开发React函数组件结合TypeScript时,使用...rest传递额外属性(如className)遇到两类类型错误:

  • 父组件传递className给Button组件时,提示Property 'className' does not exist on type 'IntrinsicAttributes & Props'
  • 组件内部访问rest.className时,提示Property 'className' does not exist on type '{ rest?: any; }'

原写法错误地在Props接口中添加rest字段接收剩余属性,不符合TypeScript对剩余属性的处理逻辑。

正确解决方案

方法1:让Props接口继承原生button元素属性

让自定义Props直接继承React.ButtonHTMLAttributes<HTMLButtonElement>,自动包含所有原生button属性(如className、onClick等),同时保留自定义属性:

import { ReactNode, ButtonHTMLAttributes } from 'react';
import className from 'classnames';
import PropTypes from 'prop-types';

// 继承原生button所有属性,添加自定义属性
interface Props extends ButtonHTMLAttributes<HTMLButtonElement> {
  children: ReactNode;
  primary?: boolean;
  secondary?: boolean;
  success?: boolean;
  warning?: boolean;
  danger?: boolean;
  outline?: boolean;
  rounded?: boolean;
}

function Button({
  children,
  primary,
  secondary,
  success,
  warning,
  danger,
  outline,
  rounded,
  // 直接解构剩余属性,无需在Props中定义rest字段
  ...rest
}: Props) {
  const classes = className(
    rest.className, // 可正常访问className
    'flex items-center px-3 py-1.5 border transition',
    {
      'border-blue-500 bg-blue-500 text-white hover:bg-blue-600': primary,
      'border-gray-800 bg-gray-800 text-white hover:bg-gray-900': secondary,
      'border-green-500 bg-green-500 text-white hover:bg-green-600': success,
      'border-yellow-500 bg-yellow-500 text-white hover:bg-yellow-600': warning,
      'border-red-500 bg-red-500 text-white hover:bg-red-600': danger,
      'rounded-full': rounded,
      'bg-white': outline,
      'text-blue-500': outline && primary,
      'text-gray-800': outline && secondary,
      'text-green-500': outline && success,
      'text-yellow-500': outline && warning,
      'text-red-500': outline && danger,
    }
  );

  if (
    Number(!!primary) +
      Number(!!secondary) +
      Number(!!success) +
      Number(!!warning) +
      Number(!!danger) >
    1
  ) {
    return (
      <button className={classes}>Please select only one variation!!!</button>
    );
  }

  return (
    <button className={classes} {...rest}>
      {children}
    </button>
  );
}

export default Button;

方法2:使用交叉类型组合自定义属性与原生属性

若不想直接继承,可通过交叉类型合并自定义属性和原生button属性:

import { ReactNode, ButtonHTMLAttributes } from 'react';

// 交叉类型:自定义属性 + 原生button属性
type Props = {
  children: ReactNode;
  primary?: boolean;
  secondary?: boolean;
  success?: boolean;
  warning?: boolean;
  danger?: boolean;
  outline?: boolean;
  rounded?: boolean;
} & ButtonHTMLAttributes<HTMLButtonElement>;

后续组件解构和使用逻辑与方法1完全一致。

关键说明

  • 不要在Props接口中定义rest字段:...rest是ES6解构语法,用于提取Props中未显式声明的剩余属性,这些属性的类型需由Props接口本身通过继承或交叉类型包含,而非作为单独字段存在。
  • 继承ButtonHTMLAttributes<HTMLButtonElement>会自动覆盖所有原生button支持的属性,无需手动声明。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.05 00:05:24