Mapped Types
Looping through keys to create dynamic type maps (the engine behind utility types).
🧑🏫 Sabse pehle — simple mein samjho#
Jaise tum JavaScript (React) mein list render karne ke liye Array.map() lagate ho — "Har item lo, usko modify karo aur return kardo". TS mein jab hum ek Type ki sab properties pe loop lagakar unko modify karke naya Type banate hain to usko Mapped Types kehte hain. Asal mein TypeScript ke saare Built-in Utility types (Partial, Required, Readonly) Mapped Types ke zariye hi likhe gaye hain!
Basic Syntax#
Mapped types can only be created using type aliases (not interfaces).
The fundamental formula is: [Key in keyof Type]
// A basic Type
type User = {
name: string;
age: number;
};
// Mapped Type: "Take all keys from User and change their value types to string"
type UserStringfied = {
[Key in keyof User]: string;
};
/* Output evaluates to:
{
name: string;
age: string;
}
*/
Creating a Custom "Partial"#
Let's build our own version of Partial (this is exactly how the TS internal library does it).
type MyPartial<T> = {
[P in keyof T]?: T[P];
// 1. Loop through all property keys of T (P in keyof T)
// 2. Append a ? to each key (making it optional)
// 3. Keep the value type exactly as it was in the original type (T[P] - this is known as an Indexed Access Type)
};
Adding / Removing Modifiers (+ and -)#
Mapped types allow you to add or remove modifiers like readonly or ?. If we wanted to take a Readonly type and make it normal (Writable) again, how would we do it? We apply the -readonly prefix (Remove readonly).
type LockedUser = {
readonly id: number;
readonly name: string;
};
type UnlockUser<T> = {
-readonly [P in keyof T]: T[P]; // This strips the readonly modifier away
};
type FreeUser = UnlockUser<LockedUser>;
/* Output:
{
id: number;
name: string;
}
*/
Note: Similarly, you can use +? (add optional) or -? (remove optional — which is exactly how the Required utility type is built).
Key Remapping (via as)#
This is slightly advanced but heavily utilized in modern TypeScript. What if you need to rename the keys while looping over them? TypeScript 4.1 introduced the as clause to allow key remapping.
type User = {
name: string;
age: number;
}
// Prepending the word "get" to every key
type UserGetters = {
[Key in keyof User as `get${Capitalize<string & Key>}`]: () => User[Key];
};
/* Output evaluates to:
{
getName: () => string;
getAge: () => number;
}
*/
Mapped types can seem daunting at first, but once you grasp the mapping loop, you can construct incredibly powerful types just like TS library authors do!