08-typescriptTermsLevel_09Mapped Types

Mapped Types

Level 9 — Advanced Types A feature that allows you to create a new Object Type by iterating (mapping) over the keys of an existing Object Type, applying transformations along the way. This is the engine that powers Utility Types.


1. Prerequisites


2. Term Category

TypeScript Advanced Type (Iterative Object Transformation Types): Mapped types ({[K in keyof T]: ...}) iterate over property keys to transform existing object types into new structures.


3. Explanation

Environment Context

  • Compile-Time

4. Common Mistakes & Pitfalls

Mistake 1: Trying to Map over an Array

The mistake: A developer writes a mapped type and passes an array literal into it: type X = MyMappedType<["name", "age"]>.

Why it's wrong: Mapped Types use in keyof T. keyof expects an Object Type or a Union of string literals. An Array is an object whose keys are 0, 1, 2, 3, so the mapped type will iterate over the array indices, not the string values inside the array! Golden Rule: Mapped Types iterate over a Union of Literal Strings ("name" | "age"), almost always generated by the keyof operator.


Mistake 2: Attempting Mapped Types inside Interface Declarations

The mistake: Writing interface Bad { [K in keyof T]: string } (TS7028).

Why it's wrong: Mapped type syntax [K in Keys] is permitted ONLY inside type alias declarations, NOT inside interface bodies.

Incorrect:

// interface Bad { [K in keyof User]: string } // ❌ An interface cannot declare a mapped type

Fix:

type Good<T> = { [K in keyof T]: string }; // Correct type alias mapped type

Mistake 3: Forgetting Mapping Modifiers + and - when Adding/Removing Readonly or Optional Modifiers

The mistake: Writing { [K in keyof T]?: T[K] } expecting to remove optionality.

Why it's wrong: To REMOVE optionality ? or readonly, use prefix - (e.g. -? or -readonly). Adding ? adds optionality.

Incorrect:

type MakeRequired<T> = { [K in keyof T]?: T[K] }; // ❌ Adds optionality instead of removing it!

Fix:

type MakeRequired<T> = { [K in keyof T]-?: T[K] }; // Correct: Strips '?' optionality

5. Practice Exercises

Exercise 1: Creating Custom ReadonlyMap<T> Mapped Types

Scenario: Re-implement the built-in Readonly<T> utility type using a mapped type syntax {[K in keyof T]: T[K]}.

Requirements:

  1. Define type MyReadonly<T> = { readonly [K in keyof T]: T[K] }.
Answer

Implementation

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

interface User {
  name: string;
  age: number;
}

type ReadonlyUser = MyReadonly<User>;
// Inferred as: { readonly name: string; readonly age: number; }

Technical Explanation

  1. Mapped types {[K in keyof T]: ...} iterate over every key in type T.
  2. Adding readonly before the brackets applies the readonly modifier to all mapped properties.
  3. Fundamental syntax for building custom object transformation utilities.

Exercise 2: Stripping Readonly and Optional Modifiers

Scenario: Create a utility type Mutable<T> that strips readonly modifiers using -readonly.

Requirements:

  1. Define type Mutable<T> = { -readonly [K in keyof T]: T[K] }.
Answer

Implementation

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

interface FrozenState {
  readonly id: string;
  readonly active: boolean;
}

type WritableState = Mutable<FrozenState>;
// Inferred as: { id: string; active: boolean; }

Technical Explanation

  1. Prefixing modifiers with - (e.g. -readonly or -?) removes those modifiers from mapped properties.
  2. Mutable<T> strips immutability constraints from all fields.
  3. Essential technique for type transformation pipelines.

Exercise 3: Mapping Properties to Nullable Unions

Scenario: Create a mapped type NullableProperties<T> that wraps every property value in a T[K] | null union.

Requirements:

  1. Define type NullableProperties<T> = { [K in keyof T]: T[K] | null }.
Answer

Implementation

type NullableProperties<T> = {
  [K in keyof T]: T[K] | null;
};

interface Profile {
  username: string;
  age: number;
}

type NullableProfile = NullableProperties<Profile>;
// Inferred as: { username: string | null; age: number | null; }

Technical Explanation

  1. Mapped types can transform the value type T[K] associated with each key K.
  2. T[K] | null converts all fields into nullable options.
  3. Standard pattern for draft form state management.


7. Key Takeaways

  • Mapped Types are a way to loop over the keys of an existing type to mathematically generate a brand new type.
  • Syntax: [Key in keyof Type]: NewValueType.
  • You can add or remove ? (optional) and readonly modifiers during the mapping process using + and -.
  • Mapped Types are the underlying engine that powers almost all built-in Utility Types (like Partial, Required, Readonly, Pick, and Omit).
Built with LogoFlowershow