TypeScript Mapped Types
Mapped types in TypeScript allow you to create new types based on existing ones by iterating over their properties and applying a transformation. They provide a concise way to manipulate and transform type definitions, reducing boilerplate and improving code maintainability.
Let’s explore how mapped types work and how they can be useful.
Basic Usage
Consider an interface representing a user:
interface User {
firstName: string;
lastName: string;
age: number;
isActive: boolean;
}
Suppose you need a new type that represents the same user data but with all properties made optional. You could manually create a new interface, but mapped types offer a more elegant solution:
type PartialUser = {
[P in keyof User]?: User[P];
};
Let’s break down this code:
[P in keyof User]
iterates over each property key (firstName
,lastName
, etc.) in theUser
interface, assigning each key to the type variableP
.User[P]
accesses the type of each property using the keyP
.?
makes each property optional in the newPartialUser
type.
Built-in Mapped Types
TypeScript provides several handy built-in mapped types:
Partial<T>
: Makes all properties ofT
optional.Required<T>
: Makes all properties ofT
required.Readonly<T>
: Makes all properties ofT
read-only.Pick<T, K>
: Creates a new type by picking specific propertiesK
fromT
.Omit<T, K>
: Creates a new type by omitting specific propertiesK
fromT
.Record<K, T>
: Defines a new type with keys of typeK
and values of typeT
.
Using the built-in Partial
would simplify our previous example:
type PartialUser = Partial<User>;
Transforming Property Types
Mapped types can also transform property types. Let’s say you want a type where all string properties of User
are converted to uppercase:
type UppercaseUser = {
[P in keyof User]: User[P] extends string ? Uppercase<User[P]> : User[P];
};
Here, we use a conditional type to check if the property type User[P]
extends string
. If it does, we apply Uppercase<User[P]>
to convert it to uppercase. Otherwise, the original property type is retained.
Practical Application
Mapped types are particularly useful when working with forms, data transformations, and creating utility types. For instance, you can use them to define types for API responses, create types for form inputs with validation, or easily transform data between different formats.
By mastering mapped types, you can write more concise, type-safe, and maintainable TypeScript code. They provide powerful tools for manipulating and transforming types, reducing boilerplate, and improving the overall structure of your projects.