如何修改TypeScript代码以禁止同时使用a和b属性?
a and b properties in TypeScript? Great question! The issue with your current code is that TypeScript’s structural typing system lets extra properties slide when checking against a union. Your D = (A | B) & C type is equivalent to (A & C) | (B & C)—so an object with both a and b still matches the A & C branch (since it has the required a and c properties), which is why d2 doesn’t throw an error.
To fix this and make TypeScript block objects that have both a and b, you need to enforce mutual exclusivity between the two properties. Here are two straightforward approaches:
Approach 1: Add exclusive never properties directly to A and B
You can modify your base types to explicitly declare that the other property must be absent (or set to never):
type A = { a: number; b?: never; } type B = { b: number; a?: never; } type C = { c: number; } type D = (A | B) & C; const d0: D = { a: 123, c: 123 } // ✅ Valid (only `a` and `c`) const d1: D = { b: 123, c: 123 } // ✅ Valid (only `b` and `c`) const d2: D = { a: 123, b: 123, c: 123 } // ❌ Error: Type '{ a: number; b: number; c: number; }' is not assignable to type 'D'
How this works:
- By adding
b?: nevertoA, we’re saying: "if this type has anaproperty, it can’t have abproperty (or if it does,bmust benever, which is unassignable to any real value)." - Similarly,
a?: neverinBensures that objects withbcan’t have a validaproperty. - Now, an object with both
aandbwon’t match eitherAorB, so it can’t be assigned toD.
Approach 2: Use a reusable exclusive type utility
If you don’t want to modify your original A and B types, you can create a generic utility type that enforces mutual exclusivity between any two types:
type A = { a: number; } type B = { b: number; } type C = { c: number; } // Utility type to make two types mutually exclusive type Exclusive<T, U> = (T & Partial<Record<keyof U, never>>) | (U & Partial<Record<keyof T, never>>); type D = Exclusive<A, B> & C; const d0: D = { a: 123, c: 123 } // ✅ Valid const d1: D = { b: 123, c: 123 } // ✅ Valid const d2: D = { a: 123, b: 123, c: 123 } // ❌ Error: Type '{ a: number; b: number; c: number; }' is not assignable to type 'D'
How this works:
- The
Exclusive<T, U>type creates two branches:T & Partial<Record<keyof U, never>>: An object that matchesT, and any properties fromUmust benever(optional, thanks toPartial).U & Partial<Record<keyof T, never>>: The reverse—an object that matchesU, with anyTproperties set tonever.
- When combined with
C, this ensures thatDonly accepts objects that have eitheraorb(but not both), plus the requiredcproperty.
Both approaches will effectively block objects that include both a and b, giving you the type safety you’re looking for.
内容的提问来源于stack exchange,提问作者Just Alex

