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'.
问题原因
- 动态标签
TagName的类型推断失败:TS无法根据to的值自动匹配Link或button对应的合法props - 直接展开所有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
相关产品推荐
相关产品推荐

