DEV Community 👩‍💻👨‍💻

Ashwin
Ashwin

Posted on

Discriminated Unions Without Common Properties

If you have two or more types without any common properties (ie., they're mutually exclusive), you can't create a discriminated union with them:


type Props = { a: boolean } | { b: string }

const obj1: Props = { a: true } // ✅ valid
const obj2: Props = { b: "hello" } // ✅ valid
const obj3: Props = { a: true, b: "hello" } // ✅ valid, but we'd like this to be an error, ie., you can either include a or b, but not both

Enter fullscreen mode Exit fullscreen mode

TS Playground

Solution

If you are dealing with two types, then you can use a custom XOR type (source):


type Without<T, U> = { [P in Exclude<keyof T, keyof U>]?: never };
type XOR<T, U> = (T | U) extends object ? (Without<T, U> & U) | (Without<U, T> & T) : T | U;

// USAGE

type Props = XOR<{ a: boolean }, { b: string }>

const obj1: Props = { a: true } // ✅ valid
const obj2: Props = { b: "hello" } // ✅ valid
const obj3: Props = { a: true, b: "hello" } // ❌ error

Enter fullscreen mode Exit fullscreen mode

TS Playground

You can alternatively use the ts-xor package to do exactly that.

If you have more than two types, you can nest XOR operators, like this:


type Props = XOR<{ c: number }, XOR<{ a: boolean }, { b: string }>>

Enter fullscreen mode Exit fullscreen mode

TS Playground

The drawback with this XOR type is that the errors shown are cryptic, so perhaps avoid this if you're creating types for a library.

Top comments (0)

Create an Account!
Now it's your turn!
 
🗒 Share a tutorial
🤔 Reflect on your coding journey
❓ Ask a question

Create an account to join hundreds of thousands of DEV members on their journey.