Articles

TypeScript Mapped Types Explained

Mapped types transform one type into another by iterating over its keys — the mechanism behind Partial, Readonly, Record, and Pick under the hood.

Takina Takina · · 4 min read
Dark-themed code editor showing TypeScript source

Mapped types are a TypeScript feature that builds a new object type by iterating over the keys of an existing one and transforming each property’s type along the way. Instead of hand-writing a type with every field spelled out, you describe a rule — “for every key in this type, do X” — and TypeScript applies it. They’re the mechanism underneath many of the built-in utility types like Partial, Readonly, and Record, and understanding them makes those utilities far less mysterious.

The basic syntax

A mapped type has the shape { [K in Keys]: Type }, where Keys is usually a union of string literals — often produced with keyof:

type Flags<T> = {
  [K in keyof T]: boolean;
};

interface FormState {
  name: string;
  email: string;
}

type FormTouched = Flags<FormState>;
// { name: boolean; email: boolean }

K in keyof T reads naturally: for each key K in the keys of T, produce a property with that same key. The value type on the right — here boolean — replaces whatever the original property held. This is fundamentally different from an interface, which lists properties one at a time; a mapped type derives them from another type, so it stays correct automatically if FormState gains or loses fields.

How Partial, Readonly, and Record are built

TypeScript’s standard library defines its most common utility types as one-line mapped types:

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

T[K] is an indexed access type — it looks up the type of property K on T, so the mapped type preserves each property’s original type rather than replacing it. This is why Partial<FormState> keeps name as string | undefined rather than turning it into something unrelated like boolean.

Modifiers: adding and removing optional and readonly

The ? and readonly modifiers can be added or stripped with +/- prefixes. + is the default and can be omitted; - explicitly removes a modifier:

type Mutable<T> = {
  -readonly [K in keyof T]: T[K];
};

type Concrete<T> = {
  [K in keyof T]-?: T[K];
};

Mutable strips readonly from every property; Concrete strips the optional ?, which is exactly how TypeScript’s built-in Required type works. Without the -, [K in keyof T]: T[K] alone just copies modifiers through unchanged — useful when you only want to remap value types, not their optionality.

Key remapping with as

TypeScript lets you rename keys during the mapping using an as clause, which is especially useful for deriving getter or event-handler names from a base type:

type Getters<T> = {
  [K in keyof T as `get${Capitalize<string & K>}`]: () => T[K];
};

type UserGetters = Getters<{ id: number; name: string }>;
// { getId: () => number; getName: () => string }

The template literal in the as clause runs for every key, so id becomes getId and name becomes getName. You can also filter keys out entirely by mapping them to never:

type OmitByValue<T, V> = {
  [K in keyof T as T[K] extends V ? never : K]: T[K];
};

Here the mapped type combines with a conditional typeT[K] extends V ? never : K — to drop any property whose value type matches V. Mapping a key to never removes it from the resulting type entirely, which is how library-authored utilities like Omit and Pick narrow down a type’s shape.

Mapped types vs interfaces

InterfacesMapped types
Definition styleExplicit, one property at a timeDerived from another type’s keys
Stays in syncManual — must edit by handAutomatic — follows the source type
Can transform valuesNoYes (apply any type expression per key)
Can rename keysNoYes, with as
Best forFixed, hand-designed shapesDeriving variants of an existing shape

Interfaces are still the right choice for a type you’re defining from scratch with no existing source to derive from. Mapped types shine when you already have a canonical type — a database row, an API response, a form’s field list — and need several transformed views of it without duplicating the field list each time.

A practical example

Combining a mapped type with generics gives you a reusable pattern for wrapping every field of an object, such as modeling API loading state per field:

type AsyncState<T> = {
  [K in keyof T]: {
    value: T[K];
    loading: boolean;
    error: string | null;
  };
};

Any interface passed into AsyncState gets a per-field loading and error wrapper without redefining the field list. This pairs well with discriminated unions when the wrapped state itself needs distinct success/error/loading variants rather than a flat object with a loading flag.

The takeaway

Mapped types let you transform an existing type’s keys and values with a single expression instead of writing out every property by hand. The core syntax — [K in keyof T]: T[K] — copies keys through unchanged; adding ?, readonly, +/- modifiers, or an as clause lets you make properties optional, immutable, renamed, or filtered out entirely. Once you recognize this pattern, Partial, Readonly, Pick, and Record stop looking like magic and start looking like four small, readable mapped types.

Takina Takina · · 4 min read

TypeScript Abstract Classes, Explained

Abstract classes in TypeScript define shared implementation plus methods subclasses must fill in. How they differ from interfaces and when to reach for them.

#TypeScript #JavaScript #Web Development
Takina Takina · · 3 min read

TypeScript's never Type, Explained

never represents values that can't exist — it marks unreachable code, exhaustive switches, and functions that always throw or loop forever.

#TypeScript #JavaScript #Web Development
Takina Takina · · 4 min read

TypeScript readonly Modifiers Explained

TypeScript's readonly keyword blocks reassignment at compile time for properties, arrays, and tuples — with no runtime enforcement at all.

#TypeScript #JavaScript #Web Development