TypeScript Mapped Types

Transform existing types into new ones

πŸ—ΊοΈ What are Mapped Types?

Mapped types transform existing types by iterating over their properties. They let you create new types based on old ones, making properties optional, readonly, or changing their types entirely.


type Readonly<T> = { readonly [K in keyof T]: T[K] };
type User = { name: string; age: number };
type ReadonlyUser = Readonly<User>;
                                    

Key Concepts

πŸ”„

Transform Properties

Modify all properties at once

type Optional<T> = {
  [K in keyof T]?: T[K];
};
πŸ”’

Add Modifiers

Make properties readonly or optional

type Readonly<T> = {
  readonly [K in keyof T]: T[K];
};
βž–

Remove Modifiers

Strip readonly or optional flags

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

Change Types

Transform property value types

type Stringify<T> = {
  [K in keyof T]: string;
};

πŸ”Ή Basic Mapped Type

Create a type that makes all properties optional:

type Person = {
  name: string;
  age: number;
  email: string;
};

type PartialPerson = {
  [K in keyof Person]?: Person[K];
};

// Result:
// {
//   name?: string;
//   age?: number;
//   email?: string;
// }

const user1: PartialPerson = { name: "Alice" };        // βœ“ Valid
const user2: PartialPerson = { age: 30 };              // βœ“ Valid
const user3: PartialPerson = {};                       // βœ“ Valid

πŸ”Ή Making Properties Readonly

Create immutable versions of types:

type Product = {
  id: number;
  name: string;
  price: number;
};

type ReadonlyProduct = {
  readonly [K in keyof Product]: Product[K];
};

const item: ReadonlyProduct = {
  id: 1,
  name: "Laptop",
  price: 999
};

console.log(item.name); // "Laptop"
// item.price = 899;    // βœ— Error: Cannot assign to readonly property

πŸ”Ή Removing Modifiers

Strip readonly or optional flags:

type OptionalUser = {
  name?: string;
  age?: number;
};

// Remove optional modifier
type RequiredUser = {
  [K in keyof OptionalUser]-?: OptionalUser[K];
};

// Result: { name: string; age: number; }

const user: RequiredUser = {
  name: "Bob",
  age: 25
};
// const invalid: RequiredUser = { name: "Bob" }; // βœ— Error: age missing

πŸ”Ή Transforming Property Types

Change all property values to a different type:

type Settings = {
  theme: string;
  fontSize: number;
  notifications: boolean;
};

// Convert all values to strings
type StringifiedSettings = {
  [K in keyof Settings]: string;
};

// Result:
// {
//   theme: string;
//   fontSize: string;
//   notifications: string;
// }

const config: StringifiedSettings = {
  theme: "dark",
  fontSize: "16",
  notifications: "true"
};

πŸ”Ή Mapped Type with Conditionals

Transform types based on conditions:

type Data = {
  id: number;
  name: string;
  active: boolean;
};

// Make only string properties optional
type OptionalStrings<T> = {
  [K in keyof T]: T[K] extends string ? T[K] | undefined : T[K];
};

type TransformedData = OptionalStrings<Data>;
// Result:
// {
//   id: number;
//   name: string | undefined;
//   active: boolean;
// }

πŸ”Ή Practical Example: API Response Handler

Create nullable versions for API responses:

type User = {
  id: number;
  username: string;
  email: string;
  profile: {
    bio: string;
    avatar: string;
  };
};

// Make all properties nullable for API responses
type Nullable<T> = {
  [K in keyof T]: T[K] | null;
};

type ApiUser = Nullable<User>;

const apiResponse: ApiUser = {
  id: 1,
  username: "alice",
  email: null,           // βœ“ Valid: can be null
  profile: {
    bio: "Hello!",
    avatar: "avatar.jpg"
  }
};

πŸ”Ή Generic Mapped Type Utility

Create reusable type transformers:

// Make all properties of type T into arrays
type Arrayify<T> = {
  [K in keyof T]: T[K][];
};

type Person = {
  name: string;
  age: number;
};

type PersonArrays = Arrayify<Person>;
// Result:
// {
//   name: string[];
//   age: number[];
// }

const people: PersonArrays = {
  name: ["Alice", "Bob"],
  age: [30, 25]
};

πŸ’‘ Key Takeaways

  • Mapped types transform existing types by iterating over properties
  • Use [K in keyof T] to iterate over all keys
  • Add modifiers with readonly or ?
  • Remove modifiers with -readonly or -?
  • Combine with conditionals for advanced transformations

🧠 Test Your Knowledge

What does [K in keyof T]?: T[K] do?