Mapped Types: Transform types using the mapped type feature.
In TypeScript, mapped types allow you to create new types by transforming properties in an existing type.
You can use mapped types to apply transformations to all properties in an object type, which is especially useful for modifying or enforcing rules across large types without needing to redefine each property manually.
Here's a breakdown of mapped types with examples.
Basic Syntax
A mapped type uses the in
keyword to iterate over keys in a given type. Here’s the general structure:
type MappedType<Type> = {[Property in keyof Type]: Type[Property];};
In this structure:
Property in keyof Type
means that we're iterating over each key in theType
.Type[Property]
accesses the type of each property.
Example Scenarios:
1. Making All Properties Optional
If you want to make all properties of an existing type optional, you can use a mapped type as follows:
type Person = {name: string;age: number;address: string;};type OptionalPerson = {[Key in keyof Person]?: Person[Key];};// Equivalent to:type OptionalPerson = {name?: string;age?: number;address?: string;};
2. Making All Properties Read-Only
To make all properties of an existing type read-only, you can add the readonly
modifier:
type ReadOnlyPerson = {readonly [Key in keyof Person]: Person[Key];};// Equivalent to:type ReadOnlyPerson = {readonly name: string;readonly age: number;readonly address: string;};
3. Creating Nullable Types
You can also make every property nullable:
type NullablePerson = {[Key in keyof Person]: Person[Key] | null;};// Equivalent to:type NullablePerson = {name: string | null;age: number | null;address: string | null;};
4. Mapping Values to Another Type
For example, mapping each property to a boolean type to represent a validation state:
type ValidationFlags<Type> = {[Property in keyof Type]: boolean;};type PersonValidation = ValidationFlags<Person>;// Equivalent to:type PersonValidation = {name: boolean;age: boolean;address: boolean;};
5. Custom Transformation of Properties
If you want to create a type where each property is a tuple of its key and value:
type TuplePerson = {[Key in keyof Person]: [Key, Person[Key]];};// Equivalent to:type TuplePerson = {name: ["name", string];age: ["age", number];address: ["address", string];};
Conditional Mapped Types
You can even add conditional logic to mapped types. For instance, changing only properties of a certain type:
type StringPropertiesToOptional<Type> = {[Property in keyof Type]: Type[Property] extends string ? Type[Property] | undefined : Type[Property];};type ModifiedPerson = StringPropertiesToOptional<Person>;// Equivalent to:type ModifiedPerson = {name?: string;age: number;address?: string;};
Summary
Mapped types provide flexibility by transforming types based on existing ones, making it easier to handle large structures and adapt them dynamically. You can make properties optional, readonly, nullable, or even map to different structures based on conditional logic, greatly improving type safety and reducing code redundancy.