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

React动态按钮组件TypeScript类型报错求助:兼容Link与onClick

解决React动态按钮组件的TypeScript类型报错问题

问题场景

重构支持两种模式的动态按钮组件:

  • 作为路由跳转链接(使用react-router-dom的Link)
  • 作为普通按钮(绑定onClick事件、指定type)
    重构为TypeScript后出现类型不兼容报错。

原代码

import React from 'react'
import { Link } from 'react-router-dom'

type Props = {
  children: React.ReactNode,
} & (
  {
    to: string,
    type: undefined,
    onClick: undefined,
  } | {
    to: undefined,
    type: 'button' | 'submit',
    onClick: (event: React.MouseEvent<HTMLButtonElement>) => void,
  }
)

export default function Button(props: Props) {
  const { children, to } = props
  const TagName = to ? Link : 'button'
  return (
    <TagName {...props}>
      {children}
    </TagName>
  )
}

报错信息

Type '{ children: ReactNode; to: string; onClick: undefined; } | { children: ReactNode; to: undefined; onClick: (event: MouseEvent<HTMLButtonElement, MouseEvent>) => void; }' is not assignable to type 'IntrinsicAttributes'.
  Type '{ children: ReactNode; to: string; onClick: undefined; }' has no properties in common with type 'IntrinsicAttributes'.

问题原因

  1. 动态标签TagName的类型推断失败:TS无法根据to的值自动匹配Link或button对应的合法props
  2. 直接展开所有props会传递冗余属性:比如给Link传递了它不接受的type/onClick(即使值为undefined),给button传递了它不接受的to,触发类型检查报错

解决方案

方案1:拆分分支明确props类型

通过类型守卫拆分两种场景,仅传递对应组件需要的属性,避免冗余属性传递:

import React from 'react'
import { Link } from 'react-router-dom'

// 拆分两种场景的props类型
type LinkButtonProps = {
  children: React.ReactNode;
  to: string;
};

type ActionButtonProps = {
  children: React.ReactNode;
  type: 'button' | 'submit';
  onClick: (event: React.MouseEvent<HTMLButtonElement>) => void;
};

type Props = LinkButtonProps | ActionButtonProps;

export default function Button(props: Props) {
  // 通过"to"属性判断当前场景
  if ('to' in props) {
    return <Link {...props}>{props.children}</Link>;
  } else {
    return <button {...props}>{props.children}</button>;
  }
}

方案2:保留原联合类型,按需传递属性

如果需要保留原Props的联合类型结构,可在渲染时筛选对应组件的合法属性:

import React from 'react'
import { Link } from 'react-router-dom'

type Props = {
  children: React.ReactNode,
} & (
  {
    to: string,
    type: undefined,
    onClick: undefined,
  } | {
    to: undefined,
    type: 'button' | 'submit',
    onClick: (event: React.MouseEvent<HTMLButtonElement>) => void,
  }
)

export default function Button(props: Props) {
  const { children, to, type, onClick } = props
  
  if (to) {
    // 仅传递Link需要的属性
    return <Link to={to}>{children}</Link>
  } else {
    // 仅传递button需要的属性
    return <button type={type} onClick={onClick}>{children}</button>
  }
}

说明

两种方案都能解决类型报错问题:

  • 方案1的类型定义更清晰,符合TS的类型窄化原则,推荐使用
  • 方案2适用于需要保留原Props结构的场景,避免大量修改类型定义

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.13 04:05:40