Mastering Mapped Types in TypeScript: Dynamic Type Transformation
Mapped types are the key to building flexible, DRY TypeScript applications. Learn how to transform types dynamically and simplify your API client logic.

Previously in this course, we explored Using keyof for Dynamic Access to safely look up object properties. Now, we're taking a significant leap forward by using that same logic to generate entire new types on the fly.
Understanding Mapped Types from First Principles
In TypeScript, a Mapped Type is a generic type that uses a union of keys (usually created via keyof) to iterate over and transform the properties of an existing type. Think of it like a map function for your data structures, but instead of transforming values at runtime, you are transforming their definitions at compile-time.
The syntax relies on the bracket notation: [P in K]: T.
P: The type variable representing the current key.K: The union of keys to iterate over.T: The new type to assign to that property.
This is the ultimate tool for reducing duplication in your codebase. Instead of manually defining a ReadonlyTask or a TaskUpdate interface, you can generate them automatically.
Worked Example: Creating a Read-Only Utility
Let’s apply this to our ongoing project: the task API client. We have a Task interface, but sometimes we want to ensure our UI components don't accidentally mutate task objects retrieved from the server.
TYPESCRIPTinterface Task { id: string; title: string; status: CE9178">'pending' | CE9178">'completed'; } // A custom Mapped Type to make all properties readonly type ReadonlyTask<T> = { readonly [P in keyof T]: T[P]; }; const myTask: ReadonlyTask<Task> = { id: CE9178">'1', title: CE9178">'Learn Mapped Types', status: CE9178">'pending' }; // myTask.title = CE9178">'New Title'; // Error: Cannot assign to CE9178">'title' because it is a read-only property.
By defining ReadonlyTask<T>, we created a reusable transformation. TypeScript iterates over every key in T, looks up the original type using indexed access T[P], and prepends the readonly modifier.
Implementing Dynamic Transformations
Mapped types also support "modifiers" that let you change the nature of the properties, not just their types. You can add readonly or make properties optional by adding a ? prefix.
Here is how we can use this to create a PartialTask for our API PATCH requests:
TYPESCRIPTtype OptionalTask<T> = { [P in keyof T]?: T[P]; }; // Now we can pass an object with only the fields we want to update const updates: OptionalTask<Task> = { status: CE9178">'completed' };
This is the underlying mechanism for built-in utility types like Partial<T> and Readonly<T>. Understanding this allows you to build sophisticated domain-specific utilities, similar to the strategies discussed in TypeScript Data Transformation: Mastering Mapped Types for API Models.
Hands-on Exercise
Refactor our Task client's "Update" functionality. Create a RequiredTask mapped type that takes a type with optional properties (like our OptionalTask above) and removes the ? modifier to force all fields to be present.
Hint: You can remove modifiers by prefixing them with a minus sign, like -readonly or -?.
Common Pitfalls
- Over-Engineering: Don't create complex mapped types for simple, static interfaces. If you only have one
Tasktype, just write the interface. Mapped types shine when you have a library of models. - Losing Type Information: Be careful when using mapped types with complex types like unions. Sometimes, the mapping can unintentionally strip or widen specific types if not handled with care.
- Readability: While powerful, they can make code harder for beginners to parse. Always document your custom utility types with a brief comment explaining their intent.
FAQ
Are Mapped Types the same as Generics? They are related, but distinct. Generics provide a "template" for a type, while Mapped Types provide the "logic" to iterate over the keys of that generic input.
When should I use a Mapped Type over an Interface? Use an interface when you know the shape of the data upfront. Use a Mapped Type when the shape is derived from another type and needs to remain in sync automatically.
Can I rename keys while mapping?
Yes, using "Key Remapping" (introduced in newer TS versions), you can use the as keyword to transform the key itself: [P in keyof T as Capitalize<string & P>]: T[P].
Recap
Mapped types allow us to transform existing types dynamically, ensuring our code remains DRY and type-safe. By using [P in keyof T], we can programmatically modify properties, add modifiers, or even transform keys, which is essential for scaling complex API clients. For further exploration of how these concepts interact with runtime logic, see Mastering TypeScript Conditional Types: A Guide to Dynamic Transformation.
Up next: We will dive into Conditional Types to add "if-else" logic to our type definitions.
Work with me

Laravel REST API Development
Clean, secure, well-documented Laravel REST APIs — the backend engine for your app, mobile client, or SaaS. Built by an API specialist.

Next.js Full-Stack Web App Development
A fast, SEO-ready full-stack web app built with Next.js 16 — from idea to deployed product, by an engineer who ships to production.


