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

TypeScript菜单联合类型未报错问题咨询

TypeScript菜单联合类型未报错问题咨询

嘿,我来帮你捋捋这个问题~

先把你的代码清晰列出来,方便理解整个场景:

type MenuItem = { title: string; href: string }
type SubMenu = { title: string; submenu: MenuItem[] }

const menu: (MenuItem | SubMenu)[] = [
  { title: "", href: "" },
  { title: "", href: "" },
  {
    title: "",
    submenu: [
      { title: "", href: "" },
      { title: "", href: "" },
      { title: "", href: "" },
    ],
  },
  // 你疑惑的这个对象,TypeScript未报错
  {
    title: "",
    href: "",
    submenu: [
      { title: "", href: "" },
      { title: "", href: "" },
      { title: "", href: "" },
    ],
  }
]

为什么这个对象不会触发报错?

这是因为TypeScript的类型系统基于鸭子类型:只要一个对象满足某个类型的所有必要属性,就会被判定为属于该类型,多余的属性不会被视为错误。

你的MenuItem和SubMenu没有设置互斥约束:

  • MenuItem只要求存在title和href,额外加个submenu属性并不会让它不符合MenuItem的定义;
  • SubMenu只要求存在title和submenu,额外加个href属性也不会让它不符合SubMenu的定义。

所以你添加的这个同时拥有href和submenu的对象,同时符合MenuItem和SubMenu两个类型,自然能被(MenuItem | SubMenu)[]这个联合数组接受,TypeScript也就不会抛出错误。

怎么让这种情况触发错误?

如果你希望MenuItem和SubMenu是互斥的——也就是一个菜单选项要么是普通项(只有href),要么是子菜单(只有submenu),可以给两个类型加上互斥属性约束,用never类型来禁止多余的属性:

// 普通菜单:允许有href,禁止有submenu
type MenuItem = { title: string; href: string; submenu?: never }
// 子菜单:允许有submenu,禁止有href
type SubMenu = { title: string; submenu: MenuItem[]; href?: never }

const menu: (MenuItem | SubMenu)[] = [
  { title: "", href: "" },
  { title: "", href: "" },
  {
    title: "",
    submenu: [
      { title: "", href: "" },
      { title: "", href: "" },
      { title: "", href: "" },
    ],
  },
  // 现在这个对象会触发类型错误
  {
    title: "",
    href: "",
    submenu: [
      { title: "", href: "" },
      { title: "", href: "" },
      { title: "", href: "" },
    ],
  }
]

这样修改后,当一个对象同时存在href和submenu时,TypeScript会检测到属性冲突:submenu在MenuItem里被约束为never(不能存在),href在SubMenu里也被约束为never,从而触发类型错误,达到你想要的校验效果。

备注:内容来源于stack exchange,提问作者Tomasz Dańczak

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.04.21 08:59:37