DEV Community

Sergey Samokhov
Sergey Samokhov

Posted on • Originally published at hoichi.io on

Simple Sum Types In TypeScript Without Common Fields

Discriminated unions are well known in TypeScript. Their only (?) downside is they need a common property usually named kind or tag, e.g.:

type RenderProps<T> =
| { kind: 'children',
    children: (x: T) => ReactElement | null;
  }
| { kind: 'render',
    render: (x: T) => ReactElement | null;
  }

Which makes it a tad too wordy to use:

const RPComponent = (props: RenderProps<number>) => {
    switch(props.kind) {
    case ('children'):
        return props.children(42);
    case ('render')
        return props.render(42);
    }
}

// elsewhere
<RPComponent kind="render" render={n => <i>{n}</i>} />

Now, I’m fine with JS in templates (if you still call of JSX as of templates, and why not), but that unnecessary kind prop gets my goat.

So here’s a more compact solution:

type RenderProps<T> =
| { children: (x: T) => ReactElement | null;
    render?: never;
  }
| { children?: never;
    render: (x: T) => ReactElement | null;
  }

const RPComponent = (props: RenderProps<number>) =>
    (props.children || props.render)(42);

// and
<RPComponent render={n => <i>{n}</i>} />

It’s still a sum type (you can neither omit both children and render nor provide them both), but now you don’t need no stinking kind anywhere.

Mind that for some reason it’s not enough to declare the union variants as { chidlren: SomeType, render: undefined }. At least for JSX, TypeScript will want you to still specify a prop and give it a value of undefined. But render?: undefined (or never, which, I think, better conveys your intention) does the trick.


Posted first at hoichi.io, because you can’t be too paranoid.

Top comments (0)

typescript

11 Tips That Make You a Better Typescript Programmer

1 Think in {Set}

Type is an everyday concept to programmers, but it’s surprisingly difficult to define it succinctly. I find it helpful to use Set as a conceptual model instead.

#2 Understand declared type and narrowed type

One extremely powerful typescript feature is automatic type narrowing based on control flow. This means a variable has two types associated with it at any specific point of code location: a declaration type and a narrowed type.

#3 Use discriminated union instead of optional fields

...

Read the whole post now!