Master TypeScript Mapped Types: Transform Your Data Structures Efficiently
Learn how to use TypeScript mapped types to create flexible and reusable data transformations with this beginner-friendly tutorial.
If you're new to TypeScript, one of its powerful features you should get familiar with is mapped types. Mapped types allow you to create new types by transforming existing ones. They are especially useful when you want to change, adapt, or apply rules to all properties of an object type without writing repetitive code.
In this tutorial, we'll learn what mapped types are, how to use them, and why they can make your TypeScript code cleaner and more maintainable.
### What are Mapped Types?
Mapped types let you iterate over properties of an existing type and apply some transformation or modifier to each property. This is done using TypeScript’s `keyof` operator along with a special syntax inside curly braces.
Here's a simple example where we create a mapped type that makes all properties of a given type optional:
type User = {
id: number;
name: string;
age: number;
};
// Mapped type that makes every property optional
type PartialUser = {
[P in keyof User]?: User[P];
};In this example, `PartialUser` is a new type where all properties from `User` (`id`, `name`, and `age`) become optional because of the `?` after `[P in keyof User]`.
### Using Built-in Mapped Types
TypeScript also provides some handy built-in mapped types like `Partial
type ReadonlyUser = Readonly<User>;
// All properties of ReadonlyUser are now read-only and cannot be changed.### Creating Your Own Mapped Types
You can also create more advanced custom mapped types. For example, let’s create a mapped type that converts all property types of a given object to strings:
type Stringify<T> = {
[P in keyof T]: string;
};
type UserAsStrings = Stringify<User>;
// UserAsStrings is { id: string; name: string; age: string; }This can be useful if you want to transform your data structures for serialization or logging purposes.
### Adding Modifiers
Mapped types support modifiers like `readonly` and `?`. You can add or remove these from the properties during transformation.
type Mutable<T> = {
-readonly [P in keyof T]: T[P];
};
// Removes readonly modifier from all propertiesHere, the `-readonly` modifier removes the readonly attribute from all properties of `T`.
### Practical Example: Deep Partial
Sometimes you want to make not just the first level of properties optional, but also nested properties inside objects. Here's how to create a `DeepPartial` mapped type:
type DeepPartial<T> = {
[P in keyof T]?: T[P] extends object ? DeepPartial<T[P]> : T[P];
};
// Usage example
interface Product {
id: number;
details: {
name: string;
description: string;
};
}
const updateData: DeepPartial<Product> = {
details: {
name: "New Name"
}
};This recursive mapped type makes every nested property optional, helping with partial updates or flexible object shapes.
### Summary
Mapped types in TypeScript let you transform existing types efficiently and with minimal code. Understanding and mastering this feature helps you write better typed, more reusable, and maintainable code. Start experimenting with mapped types using built-in utilities or create your own custom ones to fit your project's needs.