Skip to main content

What is a "mapped type"?

What a Mapped Type Is

A mapped type is a type created from another type (or a set of keys) by iterating over its keys and modifying properties (their types, names, modifiers).

You could say it is "the type-level equivalent of a for...in loop over an object's keys".


Syntax

javascript
type NewType = { [K in Keys]: Type; }
  • K is a variable representing a key;
  • in is an operator that iterates over the values of the union Keys;
  • Type is the value type we assign to every key.

Example 1. A simple mapping

javascript
type User = { name: string; age: number; }; type Nullable<T> = { [K in keyof T]: T[K] | null; }; // Transformed type: type NullableUser = Nullable<User>; /* { name: string | null; age: number | null; } */

Here:

  • keyof T is "name" | "age";
  • [K in keyof T] iterates over "name" and "age";
  • every field is assigned T[K] | null.

Example 2. Using modifiers

In mapped types you can change the modifiers of properties:

ModifierWhat it does
?makes the property optional
-?makes the property required
readonlymakes the property read-only
-readonlyremoves readonly

Example

javascript
type Optional<T> = { [K in keyof T]?: T[K]; }; type ReadonlyCopy<T> = { readonly [K in keyof T]: T[K]; }; type Mutable<T> = { -readonly [K in keyof T]: T[K]; }; type RequiredCopy<T> = { [K in keyof T]-?: T[K]; };

Example 3. Implementing the standard utilities

This is how many of the built-in utility types are constructed:

javascript
// Partial<T> type Partial<T> = { [K in keyof T]?: T[K]; }; // Required<T> type Required<T> = { [K in keyof T]-?: T[K]; }; // Readonly<T> type Readonly<T> = { readonly [K in keyof T]: T[K]; }; // Pick<T, K> type Pick<T, K extends keyof T> = { [P in K]: T[P]; }; // Record<K, T> type Record<K extends string | number | symbol, T> = { [P in K]: T; };

Example 4. Transforming keys

You can rename properties using the as construct (TypeScript >= 4.1):

javascript
type UppercaseKeys<T> = { [K in keyof T as Uppercase<string & K>]: T[K]; }; type User = { name: string; age: number; }; type UpperUser = UppercaseKeys<User>; /* { NAME: string; AGE: number; } */

Here as Uppercase<...> renames the keys to uppercase.


Example 5. Filtering keys

You can even remove keys from a type using never:

javascript
type RemoveNever<T> = { [K in keyof T as T[K] extends never ? never : K]: T[K]; }; type Example = { a: string; b: never; c: number; }; type Clean = RemoveNever<Example>; // { a: string; c: number }

Example 6. Combining with conditional types

Mapped types are often combined with conditional types for flexible transformations:

javascript
type MakeOptionalIfNullable<T> = { [K in keyof T as null extends T[K] ? K : never]?: T[K]; }; type User = { id: number; name: string | null; age: number; }; type OptionalNullable = MakeOptionalIfNullable<User>; // { name?: string | null }

Example 7. Dynamically building API types

javascript
type Events = "click" | "submit" | "hover"; type EventHandlers = { [E in Events as `on${Capitalize<E>}`]: (event: E) => void; }; /* { onClick: (event: "click") => void; onSubmit: (event: "submit") => void; onHover: (event: "hover") => void; } */

How TypeScript thinks about it

TypeScript essentially does the following:

javascript
// pseudocode let result = {}; for (let key of Keys) { result[key] = Type; } return result;

Advantages of mapped types

They let you automatically derive types They eliminate code duplication (you do not have to rewrite fields by hand) They work great with keyof, Conditional Types, Template Literals They are the basis of all built-in utility types


Summary

ConceptDescription
Mapped typeA type created by iterating over the keys of another type
Syntax[K in Keys]: Type
Extra capabilitiesreadonly, ?, as, never
Based onkeyof, in, conditional types
Examples in TSPartial, Required, Readonly, Pick, Record

Short Answer

Interview ready
Premium

A concise answer to help you respond confidently on this topic during an interview.