DEV Community 👩‍💻👨‍💻

Cover image for Typescript: Create a Union from a Type
Matías Hernández Arellano
Matías Hernández Arellano

Posted on • Originally published at matiashernandez.dev

Typescript: Create a Union from a Type

Originally written at https://matiashernandez.dev

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.

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
  }
}
Enter fullscreen mode Exit fullscreen mode

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

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.

/**
 * 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];
Enter fullscreen mode Exit fullscreen mode

Let's use it!!

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
  }
}
Enter fullscreen mode Exit fullscreen mode

Check it out in the typescript playground

Follow me on Twitter ❤️ Support my content

Top comments (0)

DEV

Thank you.

 
Thanks for visiting DEV, we’ve worked really hard to cultivate this great community and would love to have you join us. If you’d like to create an account, you can sign up here.