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]
}
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]>
}
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
}
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>
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;
}>;
}
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)
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
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!