Turn a module into a type

Let's imagine that we've got a union type that looks like this.


export type Action = "ADD_TODO" | "REMOVE_TODO" | "EDIT_TODO"

This is a common pattern in older Redux applications. Now, we've actually got a constants.ts file which has all of the elements of the union. They're even inferred by TypeScript.

The exports are not actually inferred as strings. They're inferred as the literals "ADD_TODO", "REMOVE_TODO", and "EDIT_TODO".


export const ADD_TODO = "ADD_TODO"
export const REMOVE_TODO = "REMOVE_TODO"
export const EDIT_TODO = "EDIT_TODO"

So there must be a way that we can extract this information and create the union type dynamically. Because otherwise, whenever we add a new element in constants.ts, we've also got to add it to the new place. It's just not very DRY.

So if we go to types, what we can do is we can say export type ActionModule. And we're going to use this funky little bits of TypeScript syntax.


export type ActionModule = typeof import("./constants")

You can use any kind of normal import that you would here, and it just grabs the information from constants and creates a new type out of it. So set something to the type of ActionModule, you would see that it has all of the elements in constants.ts.

in there. So if we remove one of the exports from constants, it won't be a part of these types anymore, which is quite clever.

Then we can use the trick we learned before to create a union type out of the keys of ActionModule. So we can say this:


export type ActionModule = typeof import("./constants")
export type Action = ActionModule[keyof ActionModule]

What that does is it takes the exported keys of the ActionModule, and it sort of iterates over them. So we end up with ADD_TODO, REMOVE_TODO, and EDIT_TODO which stays in sync with our actual code.

Transcript

0:00 Hello, folks. Let's imagine that we've got a union type that looks like this, where we have add to-do, remove to-do, and edit to-do as part of the union. This is a common pattern in old Redux applications. Now, this action type here, we've actually got a constants.ts file right next to it which has all of the elements of the union.

0:23 They're even inferred by TypeScript, because we have add to-do is actually not inferred as a string, it's inferred as the literal add to-do, same as remove to-do, edit to-do. There must be a way that we can extract this information and create the union type dynamically.

0:38 Otherwise, whenever we add a new element here, we've also got to add it to the new place. It's just not very dry. If we go to types, what we can do is we can say export type, action module, and we're going to use this funky little bit of TypeScript syntax, which is type of import constants.

0:57 You can use any kind of normal import that you would here. It just grabs the information from constants and creates a new type out of it. We can say export constant action module is action module, like this, and you can see that it has all of the elements in there.

1:14 If we removed one of the exports from constants, for instance, like if we just take this off, then it won't be a part of this types anymore, which is quite clever. Now, what we want to do, then, is we want to take this action module, and we want to turn it into the action below, if I just re-add this export.

1:33 The way that we can do that is we can use a trick that we've done before, which is the take the values of an object and turn them into a union, which we just do like this. We say action module, and then key of action module. What that does is it takes the keys of the action module, the exported keys, and it iterates over them, so we end up with add to-do, remove to-do, edit to-do, that will stay in sync with our actual code.

Want to turn a module into a type? You can use typeof import('./') to grab the type of any module, even third-party ones.

Here, we create a type from a constants.ts file, then map over the values to create a union.

Discuss on Twitter

More Tips

Assign local variables to default generic slots to dry up your code and improve performance