TypeScript中如何简洁定义仅含单个属性的互斥类型?
如何在TypeScript中定义「仅允许单个属性存在」的类型?
需求背景
需要定义一个包含多个可选属性的类型,但要求对象实例只能包含其中一个属性。比如原接口是这样的:
export interface OperatorExpression { equals?: any; lessThan?: any; lessThanOrEqualTo?: any; greaterThan?: any; greaterThanOrEqualTo?: any; contains?: any; // etc. }
期望的约束效果:
const op1 = { equals: {}, greaterThanOrEqualTo: {} }; // 无效:同时存在多个属性 const op2 = { lessThanOrEqualTo: {} }; // 有效:仅单个属性
现有的实现是为每个属性单独定义接口,把其他属性设为never再联合,虽然能满足需求,但新增属性时要修改所有接口,样板代码繁琐且维护成本高。
简洁实现方案
方法1:映射类型+联合类型拆分写法
先定义所有操作符的键集合,再通过映射生成每个仅含单个属性的类型,最后联合起来:
// 第一步:定义所有操作符键的集合 type OperatorKeys = 'equals' | 'lessThan' | 'lessThanOrEqualTo' | 'greaterThan' | 'greaterThanOrEqualTo' | 'contains'; // 第二步:生成仅含单个指定属性的类型 type SingleOperator<K extends OperatorKeys> = { [P in K]: any; // 目标属性设为必填 } & { [P in Exclude<OperatorKeys, K>]?: never; // 其余属性只能是never或不存在 }; // 第三步:联合所有单属性类型 type OperatorExpression = SingleOperator<OperatorKeys>;
方法2:紧凑合并写法
可以把上面的逻辑合并成一行,无需单独定义SingleOperator:
type OperatorKeys = 'equals' | 'lessThan' | 'lessThanOrEqualTo' | 'greaterThan' | 'greaterThanOrEqualTo' | 'contains'; type OperatorExpression = { [K in OperatorKeys]: { [P in K]: any } & Partial<Record<Exclude<OperatorKeys, K>, never>>; }[OperatorKeys];
效果验证
两种写法都能实现预期约束:
const op1 = { equals: {}, greaterThanOrEqualTo: {} }; // ❌ 类型错误:'greaterThanOrEqualTo'的类型不能赋值给never const op2 = { lessThanOrEqualTo: {} }; // ✅ 类型正确 const op3 = { contains: 'test' }; // ✅ 类型正确 const op4 = { equals: 123, lessThan: 456 }; // ❌ 类型错误
维护优势
新增操作符时,只需要在OperatorKeys里添加新的键名即可,比如新增notEquals:
type OperatorKeys = 'equals' | 'lessThan' | 'lessThanOrEqualTo' | 'greaterThan' | 'greaterThanOrEqualTo' | 'contains' | 'notEquals';
不需要修改其他代码,自动生成对应的约束类型,彻底解决原方案的繁琐问题。
内容的提问来源于stack exchange,提问作者Seth
相关产品推荐
相关产品推荐

