There is a simpler solution. No need to rely on any
or
complex conditional types (see answer):
- Is there a way to require component or click to be set? (Inclusive
OR
)
type MenuItemOr = {
title: string;
icon: string;
} & ({ component: object } | { click: boolean })
// brackets are important here: "&" has precedence over "|"
let testOr: MenuItemOr;
testOr = { title: "t", icon: "i" } // error, none are set
testOr = { title: "t", icon: "i", component: {} } // ✔
testOr = { title: "t", icon: "i", click: true } // ✔
testOr = { title: "t", icon: "i", click: true, component: {} } // ✔
A union type (|
) corresponds to inclusive OR
. It is intersected with the non-conditional properties.
Use the in
operator to narrow the value back to one of the constituents:
if ("click" in testOr) testOr.click // works
- Is there a way to require that both properties can't be set? (Exclusive
OR
/ XOR
)
type MenuItemXor = {
title: string;
icon: string;
} & (
| { component: object; click?: never }
| { component?: never; click: boolean }
)
let testXor: MenuItemXor;
testXor = { title: "t", icon: "i" } // error, none are set
testXor = { title: "t", icon: "i", component: {} } // ✔
testXor = { title: "t", icon: "i", click: true } // ✔
testXor = { title: "t", icon: "i", click: true, component: {} } //error,both set
Basically either component
or click
can be set, the other should never 1 be added at the same time. TS can make a discriminated union type out of MenuItemXor
, which corresponds to XOR
.
This XOR
condition for MenuItemXor
is not possible with accepted answer.
Playground
1 Technically, prop?: never
gets resolved to prop?: undefined
, though former is often used for illustration.