On this page
Utility types
What are utility types?
TypeScript ships with a set of built-in generic utility types that transform existing types into new ones. Instead of manually rewriting interfaces for every slight variation, you compose transformations on a single source of truth. The result is less code, fewer inconsistencies, and a single place to update when requirements change.
This lesson covers the most important utility types grouped by their purpose.
Transforming all properties
`Partial`
Makes every property of T optional. This is ideal for update operations where the caller provides only the fields that changed:
interface Post {
id: number;
title: string;
body: string;
published: boolean;
}
function patchPost(id: number, changes: Partial<Post>): void {
// changes can have any subset of Post properties
console.log("Applying", changes, "to post", id);
}
patchPost(1, { title: "New title" }); // Only updating the title`Required`
The opposite of Partial<T>. Every optional property becomes required. Use it when you need to validate that a configuration object is fully populated:
interface AppConfig {
apiUrl?: string;
timeout?: number;
retries?: number;
}
function startApp(config: Required<AppConfig>): void {
// Safe to use config.apiUrl without undefined checks
fetch(config.apiUrl);
}`Readonly`
Marks every property as readonly, preventing reassignment after creation. It is useful for value objects, configuration records, and constants:
interface Point {
x: number;
y: number;
}
const origin: Readonly<Point> = { x: 0, y: 0 };
// origin.x = 1; // Error: cannot assign to 'x' because it is a read-only propertySelecting and removing properties
`Pick`
Creates a new type by keeping only the keys listed in K. Use it to build view models or API responses that expose a subset of a larger model:
interface Article {
id: number;
title: string;
body: string;
authorId: number;
createdAt: Date;
tags: string[];
}
type ArticleCard = Pick<Article, "id" | "title" | "createdAt">;
// Suitable for rendering a list — no body, no internal IDs`Omit`
Creates a new type by removing the keys listed in K. Use it when it is easier to describe what you want to exclude:
type NewArticleInput = Omit<Article, "id" | "createdAt">;
// User provides everything except the server-generated fieldsBuilding dictionaries
`Record`
Creates an object type with keys of type K and values of type V. It is the typed equivalent of writing { [key: string]: V } but with more precision:
type HttpStatus = 200 | 201 | 400 | 401 | 404 | 500;
const statusMessages: Record<HttpStatus, string> = {
200: "OK",
201: "Created",
400: "Bad Request",
401: "Unauthorized",
404: "Not Found",
500: "Internal Server Error",
};
// TypeScript will error if you forget any keyFiltering union types
`Extract` and `Exclude`
These two are complementary filters on union types:
type Events = "click" | "focus" | "blur" | "mouseenter" | "mouseleave";
type MouseEvents = Extract<Events, `mouse${string}`>; // "mouseenter" | "mouseleave"
type NonMouseEvents = Exclude<Events, `mouse${string}`>; // "click" | "focus" | "blur"`NonNullable`
Removes null and undefined from a union. Useful after you have already verified that a value is present:
type OptionalId = number | null | undefined;
type DefiniteId = NonNullable<OptionalId>; // number
function processId(id: NonNullable<OptionalId>): string {
return `ID-${id}`; // No nullability check needed inside
}Introspecting function types
`ReturnType` and `Parameters`
These utility types let you extract the type information from an existing function without restating it:
function createSession(
userId: number,
device: "mobile" | "desktop",
expiresIn: number
): { token: string; expiresAt: number } {
const expiresAt = Date.now() + expiresIn * 1000;
return { token: `tok-${userId}`, expiresAt };
}
type SessionResult = ReturnType<typeof createSession>;
// { token: string; expiresAt: number }
type SessionArgs = Parameters<typeof createSession>;
// [userId: number, device: "mobile" | "desktop", expiresIn: number]
// Use Parameters to build a wrapper with identical signature
function createSessionWithLog(...args: SessionArgs): SessionResult {
console.log("Creating session for user", args[0]);
return createSession(...args);
}`Awaited`
Recursively unwraps Promise<T> until it reaches the underlying value type:
async function loadConfig(): Promise<{ theme: string; lang: string }> {
return { theme: "dark", lang: "en" };
}
type Config = Awaited<ReturnType<typeof loadConfig>>;
// { theme: string; lang: string }Practical pattern: full CRUD with utility types
Define one interface and derive all your data-transfer objects from it:
interface Product {
id: string;
name: string;
price: number;
description: string;
categoryId: string;
createdAt: Date;
updatedAt: Date;
}
// POST /products
type CreateProductDto = Omit<Product, "id" | "createdAt" | "updatedAt">;
// PATCH /products/:id
type UpdateProductDto = Partial<Omit<Product, "id" | "createdAt" | "updatedAt">>;
// GET /products (list view — no heavy description field)
type ProductSummary = Pick<Product, "id" | "name" | "price" | "categoryId">;
// Cache record keyed by product ID
type ProductCache = Record<string, Readonly<Product>>;All four types stay automatically in sync whenever the base Product interface changes.
Practice
- CRUD DTOs: Define a
BlogPostinterface with at least 6 properties. DeriveCreatePostDto,UpdatePostDto, andPostListItemtypes usingOmit,Partial, andPickrespectively. - Record usage: Create a
Record<string, number>that maps country codes to their UTC offset. Write a function that accepts this record and a country code and returns the offset with a default of 0 if the code is not found. - Extract and Exclude: Define a union type of all HTTP method strings (
"GET" | "POST" | "PUT" | "PATCH" | "DELETE" | "HEAD" | "OPTIONS"). UseExtractto produce a type containing only the methods that modify data, andExcludeto produce the read-only methods. - ReturnType and Parameters: Pick any existing function in your codebase (or write a new one). Use
ReturnTypeandParametersto extract its types and write a thin wrapper that logs the arguments before delegating to the original function.
Next lesson: conditional and mapped types — building your own utility types from scratch.
interface User {
id: number;
name: string;
email: string;
age?: number;
}
// Partial — all properties become optional
type UserDraft = Partial<User>;
// { id?: number; name?: string; email?: string; age?: number }
// Required — all properties become required (removes optional markers)
type UserComplete = Required<User>;
// { id: number; name: string; email: string; age: number }
// Readonly — all properties become read-only
type FrozenUser = Readonly<User>;
const frozen: FrozenUser = { id: 1, name: "Alice", email: "[email protected]" };
// frozen.name = "Bob"; // Error: cannot assign to read-only property
// Pick — keep only specified keys
type UserPreview = Pick<User, "id" | "name">;
// { id: number; name: string }
// Omit — remove specified keys
type UserWithoutId = Omit<User, "id">;
// { name: string; email: string; age?: number }
function updateUser(id: number, patch: Partial<User>): void {
console.log("Updating user", id, "with", patch);
}
updateUser(1, { name: "Bob" }); // Only name — perfectly valid
// Record<K, V> — creates an object type with keys K and values V
type Role = "admin" | "editor" | "viewer";
type Permissions = Record<Role, string[]>;
const perms: Permissions = {
admin: ["read", "write", "delete"],
editor: ["read", "write"],
viewer: ["read"],
};
// Extract — keep union members assignable to the filter
type StringOrNumber = string | number | boolean;
type OnlyPrimitives = Extract<StringOrNumber, string | number>;
// string | number
// Exclude — remove union members assignable to the filter
type WithoutBooleans = Exclude<StringOrNumber, boolean>;
// string | number
// NonNullable — removes null and undefined
type MaybeString = string | null | undefined;
type DefiniteString = NonNullable<MaybeString>; // string
// ReturnType and Parameters
function fetchUser(id: number, locale: string): Promise<{ name: string }> {
return Promise.resolve({ name: "Alice" });
}
type FetchReturn = ReturnType<typeof fetchUser>;
// Promise<{ name: string }>
type FetchParams = Parameters<typeof fetchUser>;
// [id: number, locale: string]
// Awaited — unwraps nested Promises
type Resolved = Awaited<Promise<Promise<string>>>; // string
Sign in to track your progress