Using Typescript Unions With Never and Extends
Tags: typescript
Published: 2021-06-12 12:51:15

---
title: Using Typescript Unions With Never and Extends
published: true
date: 2021-06-12 00:00:00 UTC
tags: typescript
description: To define conditional required component props
canonical_url: https://bionicjulia.com/blog/typescript-unions-with-never-and-extends
---

I've reached a point where I'm now really enjoying working with Typescript. I remember one of the senior engineers in my team telling me when I first started that I would find myself being a Typescript convert one day. I didn't really believe it then, as TS seemed to be so much more of a hindrance than help in the early days (it wouldn't even allow me to run my code to debug it!)...but I'm happy to say he was right. ✌🏻

I've reached the tipping point, where it now feels weird to _NOT_ have Typescript switched on for a Javascript repo. There are, for sure, still days when I'm trying to get a specific scenario typed properly and I'm finding it difficult to even know what to Google for... but it gets easier over time as you start to build a bank of knowledge and realise that the same patterns keep cropping up again and again.

One pattern that I keep coming across is this one - let's say I have a `Media` component that I need to pass certain props into, for example:

```tsx
{
  id: number;
  group: string;
  bookTitle: string;
  ebook: boolean;
  tvShowTitle: string;
  platform: string;
  songTitle: string;
  artist: string;
}
```

There are certain props that always needs to be present for the `Media` component to render, with the rest being conditional on whether certain other props are present:

```tsx
// Always required
{
  id: number;
  group: string;
}

// Also need to have either Option 1 or 2 present.

// Option 1: Both required, or neither present
{
  bookTitle: string;
  ebook: boolean;
}

// Option 2: Both required, or neither present
{
  tvShowTitle: string;
  platform: string;
}
```

How do we ask Typescript to check that if say `bookTitle` is passed as a prop, `ebook` becomes a required prop, whereas `tvShowTitle` and `platform` should NOT be present? Additionally, we want to let Typescript know that `id` and `group` should ALWAYS be required.

The answer is through a combination of union types and `extends`.

```tsx
interface MediaBase {
  id: string;
  group: string;
}

interface BookProps extends MediaBase {
  bookTitle: string;
  ebook: boolean;
  tvShowTitle: never;
  platform: never;
}

interface TvShowProps extends MediaBase {
  bookTitle: never;
  ebook: never;
  tvShowTitle: string;
  platform: string;
}

export type MediaProps = BookProps | TvShowProps;
```

`MediaProps` here is the final type of the `Media` component.

- This can either take the form of `BookProps` or `TvShowProps` (union type).
- We are explicit in our `BookProps` and `TvShowProps` using `never` to tell Typescript that these props should not be passed in if certain other props are present.
- By using `extends MediaBase`, both `BookProps` and `TvShowProps` also has `id` and `group` as required props.

Is there a better way I can do this? Let me know on [Twitter](https://twitter.com/bionicjulia) or [Instagram @bionicjulia](https://instagram.com/bionicjulia)!