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 inT.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?orreadonlydirectly).-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
| Utility | Transformation | Use Case |
|---|---|---|
Partial<T> | prop → prop? | Patching/updating data |
Required<T> | prop? → prop | Ensuring data completeness |
Readonly<T> | prop → readonly prop | Protecting data from change |
Pick<T, K> | Selects specific keys | Creating smaller sub-types |
Omit<T, K> | Removes specific keys | Stripping sensitive data (like passwords) |
Record<K, T> | Maps keys to values | Creating dictionaries or lookups |
NonNullable<T> | Removes null and undefined | Guaranteeing a value is always present |
ReturnType<T> | Extracts a function's return type | Reusing inferred return types safely |