· Jake Worth
Either/or typed props
Sometimes in a typed program, we’d like to say that a function can receive:
- One thing
- Another thing
- Never both
- Never neither
Imagine a functional component that processes user-entered copy. Sometimes it’s markdown, sometimes it’s just copy, and the typed component needs to handle all four conditions above. How is this done?
We can achieve this with a union type and optional never props:
type Props = {markdown: string; copy?: never} | {markdown?: never; copy: string};
const MaybeMarkdown = ({markdown, copy}: Props) => markdown ? parseMarkdown(markdown) : <>{copy}</>;Let’s see it in action.
// Markdown ✅console.log(MaybeMarkdown({markdown: '### Important'})); // <h3>Important</h3>
// Just copy ✅console.log(MaybeMarkdown({copy: 'Just information'})); // <>Just information</>
// Both: type error ❌console.log( MaybeMarkdown({copy: 'Just information', markdown: '### That conflicts'}),); // Type error
// Neither: type error ❌console.log(MaybeMarkdown({})); // Type errorWhy does this work and matter? Optional props alone aren’t either/or:
type Open = {markdown?: string; copy?: string};// This allows both and neither ❌Mark the other prop never, and you can’t pass it:
type Closed = {markdown?: string; copy?: never};// `copy` can't be passed ✅