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

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.27 10:18:07