如何让TypeScript禁止联合类型包含其他变体的字段?
如何让TypeScript禁止联合类型的值包含所有变体的字段?
给定如下场景:
type Foo = { foo: string; }; type Bar = { bar: string; }; type MyUnion = Foo | Bar; // 当前是允许的,但希望被禁止 const x: MyUnion = { foo: "", bar: "" };
我希望TypeScript能检测出,MyUnion类型的值同时包含foo和bar字段是不被允许的。
目前我知道两种实现方式:
方式a:转换为区分联合类型
type Foo = { _type: "foo"; foo: string; }; type Bar = { _type: "bar"; bar: string; }; type MyUnion = Foo | Bar; // 现在会被禁止,符合预期 const x: MyUnion = { _type: "foo", foo: "", bar: "" };
方式b:显式禁止其他变体的字段
type Foo = { foo: string; bar?: undefined; }; type Bar = { bar: string; foo?: undefined; }; type MyUnion = Foo | Bar; // 现在会被禁止,符合预期 const x: MyUnion = { foo: "", bar: "" };
是否存在不依赖这两种方法的其他实现方式?
当然有,你可以通过构造排除多余字段的约束类型来实现,核心思路是让联合类型的每个变体仅能包含自身必填字段,禁止出现其他变体的字段。
方案1:通用工具类型StrictUnion
可以编写一个通用工具类型,自动为联合类型的每个成员排除其他成员的字段:
type StrictUnion<T> = T extends any ? T & Partial<Record<Exclude<keyof (StrictUnion<T>), keyof T>, never>> : never; // 原类型定义保持不变 type Foo = { foo: string }; type Bar = { bar: string }; // 用StrictUnion包装原联合类型 type MyUnion = StrictUnion<Foo | Bar>; // 触发报错:类型"{ foo: string; bar: string; }"无法赋值给"StrictUnion<Foo | Bar>" const x: MyUnion = { foo: "", bar: "" }; // 合法赋值不受影响 const y: MyUnion = { foo: "" }; const z: MyUnion = { bar: "" };
这个工具类型的逻辑是:
- 遍历联合类型的每个成员
T - 提取该成员的所有键
keyof T,排除联合类型中其他成员的键 - 将这些排除的键定义为
never类型,明确禁止这些字段存在
方案2:直接交叉禁止字段(简化版)
如果联合类型成员数量不多,也可以直接为每个成员交叉一个禁止其他字段的类型,语义更直观:
type Foo = { foo: string } & { bar?: never }; type Bar = { bar: string } & { foo?: never }; type MyUnion = Foo | Bar; // 触发报错:类型"{ foo: string; bar: string; }"无法赋值给"MyUnion" const x: MyUnion = { foo: "", bar: "" };
这种方式和你提到的方式b类似,但用never替代undefined,更清晰地表达“该字段不允许存在”的语义,而非“可选为undefined”。
内容的提问来源于stack exchange,提问作者Lorentz Lasson
相关产品推荐
相关产品推荐

