TypeScript如何实现互斥属性类型并解决多余属性检查问题?
TypeScript 排他联合类型问题解决
问题背景
现有基础类型:
type Base = { mandatoryProperty: number }
需要创建Full类型,要求必须包含mandatoryProperty,且只能包含property1: number或property2: number中的一个,预期结构为:
type Full = { mandatoryProperty: number property1: number } | { mandatoryProperty: number property2: number }
最初实现的工具类型Combined:
type Combined<T, V> = { [K in keyof V]: {[R in keyof T]: T[R]} & { [P in K]: V[P] } }[keyof V]
通过type Full = Combined<Base, {property1: number, property2: number}>定义后,发现可以同时赋值property1和property2:
const X: Full = { mandatoryProperty: 3, property1: 2, property2: 4 // 未触发报错,不符合预期 }
问题1:最初的Combined为什么允许同时存在两个属性?
这不是多余属性检查的问题,核心原因是TypeScript联合类型的匹配规则:当一个对象同时满足联合类型的多个分支时,TypeScript会判定它符合该联合类型。
最初的Combined生成的Full类型是:
(Base & {property1: number}) | (Base & {property2: number})
同时包含property1和property2的对象,既满足第一个分支(拥有mandatoryProperty和property1),也满足第二个分支(拥有mandatoryProperty和property2),因此TypeScript不会报错。
问题2:修改后的带never的Combined为什么无效?
你修改后的代码:
type Combined<T, V> = { [K in keyof V]: {[R in keyof T]: T[R]} & { [S in keyof V]: S extends K ? V[K] : never } }[keyof V];
无效的原因主要有两点:
- 语法逻辑错误:
S extends K ? V[K] : never应改为S extends K ? V[S] : never——虽然当前场景下V[K]和V[S]都是number,类型结果一致,但写法逻辑错误,若V的不同属性类型不同时会直接出错。 - 必选
never属性的矛盾:该类型生成的分支要求必须提供所有V的属性,其中非目标属性的类型是never,但never类型没有合法值,TypeScript在类型检查时会出现逻辑偏差,部分旧版本TS不会触发预期的报错。
正确的解决方案
要实现严格的排他联合类型,需要在每个分支中明确禁止其他属性存在,可以通过将非目标属性设为可选的never类型来实现:
type Combined<T, V> = { [K in keyof V]: T & Pick<V, K> & Partial<Record<Exclude<keyof V, K>, never>> }[keyof V]
代码解释
Pick<V, K>:仅保留V中的当前属性KPartial<Record<Exclude<keyof V, K>, never>>:将V中除K外的其他属性设为可选且类型为never——一旦给这些属性赋值,就会触发类型错误- 最终生成的
Full类型为:(Base & {property1: number} & {property2?: never}) | (Base & {property2: number} & {property1?: never})
验证效果
此时再尝试同时赋值两个属性:
const X: Full = { mandatoryProperty: 3, property1: 2, property2: 4 // 报错:类型“number”不能赋值给类型“never” }
完全符合预期,仅允许存在property1或property2中的一个。
内容的提问来源于stack exchange,提问作者Riccardo Perego
相关产品推荐
相关产品推荐

