Typescript: Create a Union from a Type
Tags: webdev, programming, typescript
Published: 2022-11-10 03:06:32


> Originally written at [https://matiashernandez.dev](https://matiashernandez.dev/blog/post/typescript-create-a-union-from-a-type)


How can you transform a Typescript Type into a Union Type? And most important, Why would you want to do that?

A few days ago, I encountered myself refactoring a code from using multiple `useState` that handled related states into a `useReducer`.

> My rule of thumb is: If you have 3 related `useState`, you should move that to be a `useReducer`

The issue came because the state was a vast set of properties, and I wanted to cut back the time of refactoring, so I want to reuse some of the previous code.

So, I decided that I needed to create the actions for this new reducer based on the names of the State, so basically **created a union type based on the State type.**



So, the use case:

* I had a type to represent a State
* I want to create a list of actions based on that State

The goal was to do something like the following snippet.

```typescript
type State = {
  searchKeyWords: string;
  isSlidingPanelOpen: boolean;
  selectedJobDocumentId: number;
}

// I want to create this

type Actions = {
  type: 'searchKeyWords',
  payload: string
} | {
  type: 'isSlidingPanelOpen',
  payload: boolean
} | {
  type: 'selectedJobDocumentId',
  payload: number
}

/// So it can be used like this
function reducer(state: State, action: Actions) {
  switch(action.type){
    case 'searchKeyWords':
      return {
        ...state,
        searchKeyWords: action.payload
      }
    case 'isSlidingPanelOpen':
      return {
          ...state,
          isSlidingPanelOpen: action.payload
        }
    case 'selectedJobDocumentId':
      return {
        ...state,
        selectedJobDocumentId: action.payload
      }
    default:
      return state
  }
}
```

The idea is to automatically take the `State` type to create the `Actions` type.



I needed a utility type that takes in an unknown type and spits out a Union with a specific shape to achieve this behavior.

So, a clear use case for Generics and Mapped Types.



🤔 Why? Because generics are the way to write reusable types.

More on that in this Twitter thread

{% embed https://twitter.com/matiasfha/status/1590038839921283073 %}

>   
> **Mapped Types**:  
> The main idea of this is to take the properties > of a type `T` to create a new type by using those > properties in another way



Let's create a new `Unionize` utility type that will take a generic named `T` that extends an `object`.

> The `extends` keyword here acts as a way to restrict the type of `T` to be like an `object`

This type will iterate over the keys of `T` and map those to a new shape where each key of `T` will hold a new object shape.

```typescript
/**
 * Create an Union type from an Object type
 * that will use the Object key as `type` entry and the
 * Object value as `payload`
 */
type Unionize<T extends object> = {
    [k in keyof T]: { type: k; payload: T[k] };
}[keyof T];
```

Let's use it!!

```typescript
type State = {
  searchKeyWords: string;
  isSlidingPanelOpen: boolean;
  selectedJobDocumentId: number;
}

/**
 * Create an Union type from an Object type
 * that will use the Object key as `type` entry and the
 * Object value as `payload`
 */
type Unionize<T extends object> = {
    [k in keyof T]: { type: k; payload: T[k] };
}[keyof T];

// I want to create this

type Actions = Unionize<State>

/// So it can be used like this
function reducer(state: State, action: Actions) {
  switch(action.type){
    case 'searchKeyWords':
      return {
        ...state,
        searchKeyWords: action.payload
      }
    case 'isSlidingPanelOpen':
      return {
          ...state,
          isSlidingPanelOpen: action.payload
        }
    case 'selectedJobDocumentId':
      return {
        ...state,
        selectedJobDocumentId: action.payload
      }
    default:
      return state
  }
}
```

Check it out [in the typescript playground](https://tsplay.dev/wOA4lm)



Follow me on [Twitter](https://twitter.com/matiasfha)           ❤️ [Support my content](https://buymeacoffee.com/matiasfha)