Next.js+TypeScript条件渲染Link/按钮组件类型报错问题
问题分析
你遇到的类型报错:
Type 'HTMLAnchorElement' is missing the following properties from type 'HTMLButtonElement': disabled, form, formAction, formEnctype, and 11 more.ts(2322)
核心原因有三个:
- 你在函数入参位置直接解构了所有props,TypeScript对解构后的联合类型属性不会保留类型关联,仅判断
if (href)无法让TS确认当前分支属于Link类型还是button类型 - button分支的
href类型定义为undefined不够严谨,无法作为联合类型的有效判别标识 - 未收窄类型的情况下,
rest属性同时包含button专属属性(如disabled、formAction等)和a标签属性,把混合了button属性的rest传给a标签时,TS就会抛出类型不匹配的错误 - 额外添加的
if (href && linkProps)判断无实际意义,linkProps是内部声明的常量对象永远为真,对类型收窄没有任何帮助
可直接运行的修复代码
import Link, { LinkProps } from 'next/link' import { ReactNode, ButtonHTMLAttributes, AnchorHTMLAttributes, DetailedHTMLProps } from 'react' // 替换为你项目内实际的样式定义 const commonStyles = 'inline-flex items-center justify-center rounded-md px-4 py-2 text-sm' const primaryStyles = 'bg-blue-600 text-white hover:bg-blue-700' const secondaryStyles = 'bg-gray-100 text-gray-900 hover:bg-gray-200' interface BaseProps { children: ReactNode primary?: boolean className?: string } // 渲染为原生button的类型定义 type ButtonAsButtonProps = BaseProps & Omit<DetailedHTMLProps<ButtonHTMLAttributes<HTMLButtonElement>, HTMLButtonElement>, keyof BaseProps> & { href?: never } // 渲染为Next.js Link的类型定义,合并a标签原生属性 type ButtonAsLinkProps = BaseProps & LinkProps & Omit<AnchorHTMLAttributes<HTMLAnchorElement>, keyof LinkProps | keyof BaseProps> & { href: LinkProps['href'] } type ButtonProps = ButtonAsButtonProps | ButtonAsLinkProps const Button = (props: ButtonProps) => { const { children, className, primary } = props const classNames = `${commonStyles} ${primary ? primaryStyles : secondaryStyles} ${className ?? ''}` // 先判断分支收窄类型,再在分支内解构属性 if ('href' in props && props.href) { const { href, as, replace, scroll, shallow, passHref, prefetch, locale, legacyBehavior, ...restAnchorProps } = props const linkProps = { as, replace, scroll, shallow, passHref, prefetch, locale, legacyBehavior } return ( <Link href={href} {...linkProps}> <a {...restAnchorProps} className={classNames}> {children} </a> </Link> ) } // 此分支内TS自动识别为button类型,剩余属性全为button原生属性 const { ...restButtonProps } = props return ( <button {...restButtonProps} className={classNames}> {children} </button> ) } export default Button
关键修复点说明
- 把button分支的
href类型从undefined改为href?: never,这是TypeScript可辨识联合类型的标准写法,明确告知TS该分支下不会存在href属性 - 不在函数入参位置一次性解构所有属性,先通过
'href' in props做类型守卫,在分支内部再解构对应属性,TS可以在分支块内自动收窄类型,不会出现属性串扰 - 合并LinkProps和a标签原生属性时,通过
Omit排除两边重复的key,避免类型定义冲突 - 移除无意义的
linkProps真值判断,简化分支逻辑
内容的提问来源于stack exchange,提问作者Denis
相关产品推荐
相关产品推荐

