DEV Community

Cover image for Simplifying Complex TypeScript Types with the 'Unpack' Helper: Improve Developer Experience
Martin Persson
Martin Persson

Posted on • Edited on

Simplifying Complex TypeScript Types with the 'Unpack' Helper: Improve Developer Experience

Introduction

In the world of TypeScript, developers often deal with numerous types and interfaces. Modern IDEs offer substantial assistance in understanding these complex structures, yet sometimes it's still not enough. Whether dealing with extended interfaces or nested types, reading through them can become hard. That's where the 'Unpack' helper comes into play.

The Unpack Type Helper

The Unpack type helper is designed to simplify complex TypeScript interfaces, especially those involving nested or extended interfaces. Below is the definition of the helper:

type Unpack<T> = {
  [K in keyof T]: T[K] extends object ? Unpack<T[K]> : T[K]
}
Enter fullscreen mode Exit fullscreen mode

Alternatively, you can use a shorter version, thanks to Olaoluwa Mustapha for pointing out that mapped types will return their argument as-is if it's a primitive type:

type UnpackAlt<T> = {
  [K in keyof T]: UnpackAlt<T[K]>
}
Enter fullscreen mode Exit fullscreen mode

How it Works

The Unpack type accepts a generic type parameter T, representing the complex interface you want to simplify.

By using the [K in keyof T], the helper iterates over all keys within the specified type.

The ternary condition T[K] extends object ? Unpack : T[K] checks if the current property is an object. If it is, the type helper recursively applies itself, drilling down into nested objects. If it's not an object, the property type is simply returned.

Example

Working with complex interfaces that extend other interfaces or contain nested structures can sometimes lead to a lack of clarity. Let's look at a concrete example to understand how the Unpack type helper can help.

Consider the following interfaces:

interface Base {
  _id: string
}

interface Info {
  contact: {
    email: string
  }
  socials: {
    twitter: string
  }
}

interface User extends Base, Info {
  name: string
}
Enter fullscreen mode Exit fullscreen mode

These interfaces might be common in a user management system. If you try to hover over a variable of type User, you may only see Interface User, without a clear breakdown of its structure.

Now, let's use the Unpack type helper:

type UnpackedUser = Unpack<User>
Enter fullscreen mode Exit fullscreen mode

By creating an UnpackedUser type, you can hover over it and see the fully expanded structure:

type UnpackedUser = {
    name: string;
    _id: string;
    contact: Unpack<{
        email: string;
    }>;
    socials: Unpack<{
        twitter: string;
    }>;
}
Enter fullscreen mode Exit fullscreen mode

Notice how the Unpack type helper breaks down extended and nested interfaces, providing a clear view of the structure. This enhances understanding and maintainability, offering a streamlined solution to working with complex TypeScript types.

Top comments (2)

Collapse
 
olaolu profile image
Olaoluwa Mustapha

This was an awesome read! Thanks!!

It turns out you might not need the extra conditional statement as Mapped Types, by default, return their argument as-is if it's a primitive type: tsplay.dev/WoMJaN

Collapse
 
martinpersson profile image
Martin Persson

Thank you! Glad you liked it.

I had no idea about the default return for mapped types, that's really cool. I will update the post and include your version!