DEV Community

glocore
glocore

Posted on

4 1

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)

AWS Security LIVE!

Join us for AWS Security LIVE!

Discover the future of cloud security. Tune in live for trends, tips, and solutions from AWS and AWS Partners.

Learn More