Last updated: 2026-05-05

Mapped type and utility types

Mapped Types and Utility Types are the "factory tools" of TypeScript. They allow you to take an existing type and automatically transform it into a new one without manually retyping every property.


Mapped Types

A Mapped Type acts like a map() function for types. It iterates through the keys of an existing type and applies a rule to each one.

Basic Syntax:

type MappedType<T> = {
  [K in keyof T]: T[K];
};
  • keyof T: Gets a union of all property names in T.
  • K in ...: Iterates through each property name.
  • T[K]: Accesses the original type of that property.

Examples of Transformation:

1. Making Properties Optional — Adding ? after each key:

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

type OptionalUser = {
  [K in keyof User]?: User[K];
};
// Result: { name?: string; age?: number; email?: string }

2. Making Properties Readonly — Adding readonly before each key:

type ReadonlyUser = {
  readonly [K in keyof User]: User[K];
};
// Result: { readonly name: string; readonly age: number; readonly email: string }

Modifiers: Adding and Removing with + and -

Mapped types also support modifier operators that can explicitly add or remove readonly and ? from properties. This is how built-in utility types like Required<T> work under the hood.

  • + adds a modifier (default behaviour — same as writing ? or readonly directly).
  • - removes a modifier from every property.
// Removes optional (?) from all properties — equivalent to Required<T>
type Concrete<T> = {
  [K in keyof T]-?: T[K];
};

// Removes readonly from all properties
type Mutable<T> = {
  -readonly [K in keyof T]: T[K];
};

Understanding -? and -readonly explains how TypeScript's built-in utility types are implemented, and gives you the tools to write custom transformations when the built-ins don't quite fit.


Utility Types

TypeScript provides several built-in Utility Types that perform these transformations for you. They are essentially pre-written mapped types that handle common scenarios.

1. Partial<T> and Required<T>

  • Partial: Makes every property optional. Useful for "Update" forms where you might only change one or two fields.
  • Required: The opposite — forces every property to be present, even if they were marked with ? in the original interface. Internally uses the -? modifier covered above.

2. Readonly<T>

Locks the object. Once it is created, none of its properties can be reassigned. Perfect for configuration objects or constants.

3. Pick<T, K> and Omit<T, K>

These allow you to "slice" your types to get exactly what you need.

  • Pick: You choose which keys to keep.
  • Omit: You choose which keys to remove.
interface User { id: number; name: string; email: string; password: string; }

// We only want the public info
type PublicProfile = Pick<User, "name" | "email">;

// We want everything EXCEPT the password
type UserWithoutPassword = Omit<User, "password">;

4. Record<K, T>

The cleanest way to define an object that acts as a map or dictionary, where you define exactly what the Keys (K) and Values (T) should be.

type Page = "home" | "about" | "contact";
const nav: Record<Page, string> = {
  home: "/home",
  about: "/about",
  contact: "/contact"
};

5. NonNullable<T>

Strips null and undefined from a type. Useful when you want to guarantee a value is always present.

type MaybeString = string | null | undefined;
type DefiniteString = NonNullable<MaybeString>;
// Result: string

6. ReturnType<T>

Extracts the return type of a function without having to declare it manually. Useful when working with functions whose return types are complex or inferred.

function getUser() {
  return { id: 1, name: "Ali", email: "ali@example.com" };
}

type User = ReturnType<typeof getUser>;
// Result: { id: number; name: string; email: string }

Summary Table

UtilityTransformationUse Case
Partial<T>propprop?Patching/updating data
Required<T>prop?propEnsuring data completeness
Readonly<T>propreadonly propProtecting data from change
Pick<T, K>Selects specific keysCreating smaller sub-types
Omit<T, K>Removes specific keysStripping sensitive data (like passwords)
Record<K, T>Maps keys to valuesCreating dictionaries or lookups
NonNullable<T>Removes null and undefinedGuaranteeing a value is always present
ReturnType<T>Extracts a function's return typeReusing inferred return types safely