Skip to content
Make two prop shapes mutually exclusive

    ↑↓ move · ⏎ open · esc close

    Patterns

    How do I accept either `href` or `onClick`, but reject objects that provide both?

    Union the two shapes and add the other side’s unique keys as optional `never` properties.

    The recipe

    type Without<T, U> = {
      [K in Exclude<keyof T, keyof U>]?: never
    }
    
    type XOR<T, U> =
      | (T & Without<U, T>)
      | (U & Without<T, U>)
    
    type LinkProps = XOR<
      { href: string },
      { onClick: () => void }
    >

    The build compiles this and checks each result below.

    How it works

    1. 01
      [K in Exclude<keyof T, keyof U>]?: never

      Unique keys from the opposite branch may be absent, but can never hold a value.

    2. 02
      | (U & Without<T, U>)

      Each union member combines its required shape with a ban on the other one.

    What you get

    Where it goes wrong

    This is clearest for two small object shapes. Large overlapping types produce noisy diagnostics, and `exactOptionalPropertyTypes` changes whether an explicit `undefined` can satisfy optional `never`.

    Takeaway

    Use optional `never` to say that a property belongs to the other branch and must not appear here.

    See also

    Patterns