You can use the Union combinator.
const decoder = pipe(
D.struct({
a: D.string
}),
D.intersect(
D.union(
D.struct({ b: D.string }),
D.struct({ c: D.string })
)
)
);
Bear in mind that you won't be able to access b
or c
without first checking if those properties exist, since Typescript doesn't have a way to know which one of the two is present in your object.
type Decoder = D.TypeOf<typeof decoder>
declare const myDecoder: Decoder;
myDecoder.a // inferred as `string`
myDecoder.b // TYPE ERROR: Property 'b' does not exist on type '{ a: string; } & { c: string; }'
myDecoder.c // TYPE ERROR: Property 'c' does not exist on type '{ a: string; } & { b: string; }'
if ("b" in myDecoder) {
myDecoder.b // inferred as `string`
}
if ("c" in myDecoder) {
myDecoder.c // inferred as `string`
}
Notice how, when checking for both the mutually exclusive properties, you get a type error. TypeScript correctly infers that it's a case that can never happen (myDecoder
is inferred as never
inside the if
block)
if ("b" in myDecoder && "c" in myDecoder) {
myDecoder.b // TYPE ERROR: Property 'b' does not exist on type 'never'.
myDecoder.c // TYPE ERROR: Property 'c' does not exist on type 'never'.
}