Typescript: Create a Union from a Type
Matías Hernández Arellano
Posted on November 10, 2022
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 auseReducer
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
}
}
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 typeT
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 ofT
to be like anobject
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];
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
}
}
Check it out in the typescript playground
Follow me on Twitter ❤️ Support my content
Posted on November 10, 2022
Join Our Newsletter. No Spam, Only the good stuff.
Sign up to receive the latest update from our blog.
Related
November 28, 2024
October 4, 2024