TypeScript Mapped Types
Transform existing types into new ones
πΊοΈ What are Mapped Types?
Mapped types transform existing types by iterating over their properties. They let you create new types based on old ones, making properties optional, readonly, or changing their types entirely.
type Readonly<T> = { readonly [K in keyof T]: T[K] };
type User = { name: string; age: number };
type ReadonlyUser = Readonly<User>;
Key Concepts
Transform Properties
Modify all properties at once
type Optional<T> = {
[K in keyof T]?: T[K];
};
Add Modifiers
Make properties readonly or optional
type Readonly<T> = {
readonly [K in keyof T]: T[K];
};
Remove Modifiers
Strip readonly or optional flags
type Mutable<T> = {
-readonly [K in keyof T]: T[K];
};
Change Types
Transform property value types
type Stringify<T> = {
[K in keyof T]: string;
};
πΉ Basic Mapped Type
Create a type that makes all properties optional:
type Person = {
name: string;
age: number;
email: string;
};
type PartialPerson = {
[K in keyof Person]?: Person[K];
};
// Result:
// {
// name?: string;
// age?: number;
// email?: string;
// }
const user1: PartialPerson = { name: "Alice" }; // β Valid
const user2: PartialPerson = { age: 30 }; // β Valid
const user3: PartialPerson = {}; // β Valid
πΉ Making Properties Readonly
Create immutable versions of types:
type Product = {
id: number;
name: string;
price: number;
};
type ReadonlyProduct = {
readonly [K in keyof Product]: Product[K];
};
const item: ReadonlyProduct = {
id: 1,
name: "Laptop",
price: 999
};
console.log(item.name); // "Laptop"
// item.price = 899; // β Error: Cannot assign to readonly property
πΉ Removing Modifiers
Strip readonly or optional flags:
type OptionalUser = {
name?: string;
age?: number;
};
// Remove optional modifier
type RequiredUser = {
[K in keyof OptionalUser]-?: OptionalUser[K];
};
// Result: { name: string; age: number; }
const user: RequiredUser = {
name: "Bob",
age: 25
};
// const invalid: RequiredUser = { name: "Bob" }; // β Error: age missing
πΉ Transforming Property Types
Change all property values to a different type:
type Settings = {
theme: string;
fontSize: number;
notifications: boolean;
};
// Convert all values to strings
type StringifiedSettings = {
[K in keyof Settings]: string;
};
// Result:
// {
// theme: string;
// fontSize: string;
// notifications: string;
// }
const config: StringifiedSettings = {
theme: "dark",
fontSize: "16",
notifications: "true"
};
πΉ Mapped Type with Conditionals
Transform types based on conditions:
type Data = {
id: number;
name: string;
active: boolean;
};
// Make only string properties optional
type OptionalStrings<T> = {
[K in keyof T]: T[K] extends string ? T[K] | undefined : T[K];
};
type TransformedData = OptionalStrings<Data>;
// Result:
// {
// id: number;
// name: string | undefined;
// active: boolean;
// }
πΉ Practical Example: API Response Handler
Create nullable versions for API responses:
type User = {
id: number;
username: string;
email: string;
profile: {
bio: string;
avatar: string;
};
};
// Make all properties nullable for API responses
type Nullable<T> = {
[K in keyof T]: T[K] | null;
};
type ApiUser = Nullable<User>;
const apiResponse: ApiUser = {
id: 1,
username: "alice",
email: null, // β Valid: can be null
profile: {
bio: "Hello!",
avatar: "avatar.jpg"
}
};
πΉ Generic Mapped Type Utility
Create reusable type transformers:
// Make all properties of type T into arrays
type Arrayify<T> = {
[K in keyof T]: T[K][];
};
type Person = {
name: string;
age: number;
};
type PersonArrays = Arrayify<Person>;
// Result:
// {
// name: string[];
// age: number[];
// }
const people: PersonArrays = {
name: ["Alice", "Bob"],
age: [30, 25]
};
π‘ Key Takeaways
- Mapped types transform existing types by iterating over properties
-
Use
[K in keyof T]to iterate over all keys -
Add modifiers with
readonlyor? -
Remove modifiers with
-readonlyor-? - Combine with conditionals for advanced transformations