TypeScript联合类型为何允许所有指定键?如何实现严格互斥联合?
实现TypeScript严格互斥联合类型
你遇到的问题源于TypeScript默认联合类型的包容特性——只要对象符合联合中任意一个成员的结构,就会被判定为合法,因此需要通过额外约束实现严格互斥:对象只能匹配联合类型中的某一个成员,不能同时包含多个成员的属性。
解决方案1:显式添加互斥属性的never约束
直接给每个联合成员添加对方属性的never类型(设为可选,避免无属性时的报错),当对象同时包含跨成员属性时,会触发类型冲突:
type Person = // 仅允许id,禁止firstName/lastName | { id: string; firstName?: never; lastName?: never } // 仅允许firstName/lastName,禁止id | { firstName: string; lastName: string; id?: never };
验证示例:
// 合法:仅包含id const validPerson1: Person = { id: 'user-123' }; // 合法:仅包含名字属性 const validPerson2: Person = { firstName: 'Alice', lastName: 'Smith' }; // 报错:Type 'string' is not assignable to type 'never'(同时包含id和名字属性) const invalidPerson1: Person = { id: 'user-456', firstName: 'Bob', lastName: 'Brown' }; // 报错:未知属性foo(符合预期) const invalidPerson2: Person = { firstName: 'Charlie', lastName: 'Davis', foo: 'extra' };
解决方案2:封装通用工具类型
如果需要复用严格互斥逻辑,可以封装工具类型StrictUnion,自动处理联合类型的互斥约束:
type StrictUnion<T extends object> = T extends infer U ? U & { [K in Exclude<keyof T, keyof U>]?: never } : never; // 用工具类型定义目标类型 type Person = StrictUnion<{ id: string } | { firstName: string; lastName: string }>;
这个工具类型的原理是:对联合类型的每个成员U,自动添加其他成员的属性并将其类型设为never,从而禁止跨成员属性共存。
原理说明
TypeScript的联合类型是包容式的——只要对象满足任意一个成员的结构要求,就会被视为合法。通过添加never类型的互斥属性,我们强制对象只能匹配联合中的某一个成员:若同时包含其他成员的属性,属性类型会发生冲突(string无法赋值给never),触发类型检查错误。
内容的提问来源于stack exchange,提问作者Noitidart
相关产品推荐
相关产品推荐

