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

TypeScript中能否定义严格二选一联合类型实现属性互斥校验?

你当前的类型定义不会报错的核心原因是,TypeScript的非严格联合类型不会校验分支中未定义的属性:Circle接口仅要求必须存在radius、kind可选,没有限制不能出现sideLength,所以同时带有radius和sideLength的对象会被判定为符合Circle类型,自然不会触发报错。

解决方案

我们可以通过排他联合类型的写法,给每个分支明确标记其他分支专属属性为never,即可实现你需要的校验效果,修改后的类型定义如下:

interface Circle {
  kind?: "circle";
  radius: number;
  // 标记Square的专属属性不允许在Circle分支出现
  sideLength?: never;
}
 
interface Square {
  kind: "square";
  sideLength: number;
  // 标记Circle的专属属性不允许在Square分支出现
  radius?: never;
}
 
type Shape = Circle | Square;

上述类型完全符合你给出的所有校验规则:

  • 携带kind: 'circle' + radius的对象:匹配Circle类型,校验通过
  • 仅携带radius的对象:匹配Circle类型(kind可选),校验通过
  • 携带kind: 'square' + sideLength的对象:匹配Square类型,校验通过
  • 同时携带sideLength和radius的对象:既不符合Circle(存在不允许的sideLength)也不符合Square(存在不允许的radius),校验报错
  • 仅携带sideLength的对象:不符合Circle(缺少必填radius)也不符合Square(缺少kind: 'square'),校验报错
  • 携带kind: 'square' + radius的对象:不符合Square的radius?: never限制,校验报错
  • 携带kind: 'circle' + sideLength的对象:不符合Circle的sideLength?: never限制,校验报错

实际场景适配说明

对应你提到的styled-componentsas属性场景,你可以按照相同逻辑定义:将默认的button属性作为第一个分支(as?: 'button'),a标签属性作为第二个分支(as: 'a'),互相标记对方的专属属性为never即可实现:不传as时仅允许传入button属性,传as="a"时仅允许传入a标签属性,交叉传入属性时直接触发TS报错。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.10.07 10:54:04