Mapped Types¶
A mapped type iterates over the keys of another type and produces a new type.
The basic syntax:
type NewType<T> = {
[K in keyof T]: T[K]
}
This means:
- “For each key
KinT, produce a property with the same key and value type.”
Let’s explore how to use this pattern to transform types.
Transforming Object Types¶
Mapped types allow you to modify:
- property types
- property modifiers (
readonly,?) - keys
- value shapes
This is how TypeScript’s built‑in utility types work.
Example: Make all properties optional¶
type MyPartial<T> = {
[K in keyof T]?: T[K]
}
Usage:
type User = { id: string; name: string }
type UserUpdate = MyPartial<User>
// { id?: string; name?: string }
This is exactly how Partial<T> is implemented.
Example: Make all properties readonly¶
type MyReadonly<T> = {
readonly [K in keyof T]: T[K]
}
Usage:
type Config = { port: number; debug: boolean }
type FrozenConfig = MyReadonly<Config>
// { readonly port: number; readonly debug: boolean }
Example: Remove readonly¶
type Mutable<T> = {
-readonly [K in keyof T]: T[K]
}
The -readonly modifier removes the readonly flag.
Example: Remove optional¶
type RequiredProps<T> = {
[K in keyof T]-?: T[K]
}
The -? modifier removes optionality.
Transforming Value Types¶
Mapped types can also transform the value types.
Example: Convert all properties to booleans¶
type Flags<T> = {
[K in keyof T]: boolean
}
Usage:
type Permissions = { read: string; write: string }
type PermissionFlags = Flags<Permissions>
// { read: boolean; write: boolean }
Example: Wrap all properties in a Promise¶
type Asyncify<T> = {
[K in keyof T]: Promise<T[K]>
}
Usage:
type User = { id: number; name: string }
type AsyncUser = Asyncify<User>
// { id: Promise<number>; name: Promise<string> }
This is extremely useful for API layers and async transformations.
Key Remapping (as)¶
TypeScript 4.1 introduced key remapping, which allows you to change the keys of a mapped type.
This unlocks powerful transformations.
Example: Prefix all keys¶
type PrefixKeys<T, P extends string> = {
[K in keyof T as `${P}${string & K}`]: T[K]
}
Usage:
type User = { id: string; name: string }
type ApiUser = PrefixKeys<User, "api_">
// { api_id: string; api_name: string }
Example: Filter keys¶
You can remove keys by mapping them to never.
type RemoveFunctions<T> = {
[K in keyof T as T[K] extends Function ? never : K]: T[K]
}
Usage:
type Mixed = {
id: string
log(): void
}
type NoFunctions = RemoveFunctions<Mixed>
// { id: string }
This is how you build “pick all non‑function properties” utilities.
Example: Convert keys to uppercase¶
type UppercaseKeys<T> = {
[K in keyof T as Uppercase<string & K>]: T[K]
}
Usage:
type User = { id: string; name: string }
type UpperUser = UppercaseKeys<User>
// { ID: string; NAME: string }
Combining Key Remapping + Value Transformation¶
Mapped types can do both at once.
Example: Create getter methods for each property¶
type Getters<T> = {
[K in keyof T as `get${Capitalize<string & K>}`]: () => T[K]
}
Usage:
type User = { id: number; name: string }
type UserGetters = Getters<User>
// {
// getId: () => number
// getName: () => string
// }
This is how ORMs and state libraries generate dynamic APIs.
Putting It All Together¶
Here’s a realistic example combining everything:
type Model<T> = {
[K in keyof T as `get${Capitalize<string & K>}`]: () => T[K]
} & {
[K in keyof T as `set${Capitalize<string & K>}`]: (value: T[K]) => void
}
Usage:
type User = { id: string; age: number }
type UserModel = Model<User>
Result:
{
getId(): string
getAge(): number
setId(value: string): void
setAge(value: number): void
}
This is the foundation of:
- ORMs
- form builders
- state management libraries
- code generation tools
Mapped types are the engine behind many advanced TypeScript patterns.
Summary¶
In this lesson, you learned how mapped types let you transform object types:
1. Transforming object types¶
- add/remove
readonly - add/remove optionality
- change value types
- wrap values in other types
2. Key remapping¶
- rename keys
- prefix/suffix keys
- filter keys using
never - generate new APIs dynamically
Mapped types are one of the most expressive features in TypeScript—they let you reshape types with the same flexibility that JavaScript gives you for reshaping objects.