Mapped Types
Transform every property of a type with key remapping and modifiers
Overview
Mapped types iterate over the keys of an existing type to produce a new one, applying a transformation to each property. They use the [K in keyof T] syntax and support modifiers like readonly and ?, plus key remapping via as. Utility types such as Partial, Readonly, and Record are all mapped types.
Syntax / Usage
Iterate over keyof T and optionally add or remove modifiers. A leading + or - explicitly adds or strips readonly/optional.
// Make every property optional
type MyPartial<T> = {
[K in keyof T]?: T[K]
}
// Make every property required and mutable
type Concrete<T> = {
-readonly [K in keyof T]-?: T[K]
}
interface Config {
readonly host?: string
readonly port?: number
}
type FullConfig = Concrete<Config>
// { host: string; port: number }
Examples
Remap keys with as to build a set of event handler names.
type Handlers<T> = {
[K in keyof T as `on${Capitalize<string & K>}`]: (value: T[K]) => void
}
interface State {
count: number
name: string
}
type StateHandlers = Handlers<State>
// { onCount: (value: number) => void; onName: (value: string) => void }
Filter out properties by remapping unwanted keys to never.
type OnlyStrings<T> = {
[K in keyof T as T[K] extends string ? K : never]: T[K]
}
type S = OnlyStrings<{ id: number; name: string; slug: string }>
// { name: string; slug: string }
Build a lookup type from a union of keys.
type Flags<K extends string> = {
[P in K]: boolean
}
type FeatureFlags = Flags<"beta" | "darkMode"> // { beta: boolean; darkMode: boolean }
Common Mistakes
- Forgetting
keyof, iterating over a value instead of its keys - Omitting the
-prefix when you intend to stripreadonlyor optional - Remapping to
neveraccidentally, silently dropping properties - Using a mapped type where a simple
Recordwould be clearer - Expecting method implementations to survive—only the type shape is mapped
See Also
typescript-conditional-types utility-types generics