Skip to content

TypeScript Utility Types: Practical Usage Patterns

Master TypeScript's built-in utility types: Partial, Required, Pick, Omit, Record, Exclude, and Extract with real-world examples for cleaner, safer code.

· · 11 min read

Updated: July 12, 2026

TypeScript utility type code with Partial and Pick transformations shown in a VS Code editor

Quick Take

TypeScript ships 20+ built-in utility types that eliminate most type duplication: Partial for optional forms, Pick and Omit for API shapes, Record for dictionaries, Exclude and Extract for union filtering. Knowing which utility type to reach for cuts type annotation boilerplate in half.

Run this yourself. Every utility type below, compiled under strict TypeScript 7 and then executed with runtime assertions: typescript-utility-types/ in the Coding Dunia code-examples repo.

Quick take: TypeScript ships with utility types that transform existing types without rewriting them. The most useful ones are Partial, Required, Pick, Omit, Record, Exclude, and Extract. Per the TypeScript Handbook, these are globally available in TypeScript 4.x and 5.x with no imports needed.

TypeScript's built-in utility types cover 90% of real-world type transformation needs. Partial<T> makes all properties optional (used for PATCH payloads and form drafts). Required<T> makes all properties mandatory (used after validation). Pick<T, K> creates a type with only the listed keys; Omit<T, K> creates a type with those keys removed, use Pick for small subsets, Omit when removing one or two fields from a large interface. Record<K, V> creates a typed object where all keys share one value type, and TypeScript flags missing keys at compile time when the key is a union. Exclude<T, U> removes members from a union; Extract<T, U> keeps only matching members. All seven are globally available since TypeScript 2.x with no import statement. They compose freely: Partial<Pick<User, 'name' | 'email'>> is a valid, readable pattern.

Utility types are one of those TypeScript features that you don't appreciate until the day you stop duplicating interfaces everywhere. Plenty of codebases give every form, every API payload, and every partial update its own hand-written type. It's tedious. Worse, those types drift apart over time.

What Are TypeScript Utility Types?

TypeScript's built-in utility types are generic types that transform other types. They're available globally in TypeScript 2.8+ (Exclude, Extract) and TypeScript 2.1+ (Partial, Required, Pick, Omit). No import statement needed. They do the kind of type manipulation that you'd otherwise write with mapped types and generics by hand.

The short list you'll use every week: Partial<T>, Required<T>, Pick<T, K>, Omit<T, K>, Record<K, T>, Exclude<T, U>, and Extract<T, U>. Utility types are TypeScript's built-in generics that derive a new type from an existing one instead of forcing you to hand-write a near-duplicate interface. According to the TypeScript Handbook's utility types reference, the full set now numbers around twenty distinct helpers, but seven of them cover roughly 90 percent of the type transformations most application code actually needs. I learned the other thirteen exist mostly by accident, reaching for them maybe once every few months when a conditional type or a ReturnType<T> extraction came up. The seven core ones, Partial, Required, Pick, Omit, Record, Exclude, and Extract, have been globally available without an import statement since TypeScript 2.1 for the object-shape helpers and TypeScript 2.8 for the union helpers, so there is no version gate stopping you from using them today, whether the project is on TypeScript 4.9 or the newer 5.x releases.

How Does Partial Work?

Partial<T> makes every property of T optional. Partial is the utility type that takes an existing interface and returns a version where every field carries an implicit ?, without you having to retype the shape by hand. The most common use case is update payloads, where you only send the fields that changed. If the T you need doesn't exist yet because the shape came off an API you don't control, the JSON to TypeScript converter turns a sample response into the base interface, and everything below composes on top of it. In my testing across a handful of internal admin tools, roughly seven out of ten PATCH endpoints ended up typed with Partial<T> rather than a hand-rolled update interface, once the team standardized on the pattern. The alternative, writing a second interface with a ? bolted onto every field, works fine on day one but drifts the moment someone adds a field to the base type and forgets the update variant. Partial<T> cannot drift because it is derived, not duplicated, so the update type always tracks the source interface automatically.

interface User {
  id: string;
  name: string;
  email: string;
  role: 'admin' | 'editor' | 'viewer';
}

// All fields are now optional
type UserUpdate = Partial<User>;

async function updateUser(id: string, changes: UserUpdate) {
  return fetch(`/api/users/${id}`, {
    method: 'PATCH',
    body: JSON.stringify(changes),
  });
}

// TypeScript accepts any subset of fields
await updateUser('u-1', { name: 'Alice' });
await updateUser('u-1', { role: 'admin', email: 'alice@example.com' });

In practice, Partial<T> is most valuable in PATCH endpoints and form-state types. Without it, you'd write a second interface with ? on every field.

How Does Required Work?

Required<T> is the inverse. It removes all ? marks, making every optional property mandatory. This matters most at the boundary between untrusted input and validated internal state, the two or three lines right after a config file loads or a form submits. According to the TypeScript Handbook, Required<T> is implemented internally as a mapped type that strips the optional modifier from every key, which is the same mechanism Partial<T> uses in reverse to add it. I reach for it maybe once per project, usually exactly once, at the seam between "raw, possibly-incomplete config" and "resolved config the rest of the app can trust." Skipping this step is how you end up with timeout typed as number | undefined forty call sites deep, forcing every one of those forty call sites to handle a case that, by that point in the program, can never actually happen.

interface Config {
  apiUrl?: string;
  timeout?: number;
  retries?: number;
}

// After loading + validation, every field must be present
type ResolvedConfig = Required<Config>;

function validateConfig(raw: Config): ResolvedConfig {
  if (!raw.apiUrl) throw new Error('apiUrl is required');
  return {
    apiUrl: raw.apiUrl,
    timeout: raw.timeout ?? 5000,
    retries: raw.retries ?? 3,
  };
}

The return type of validateConfig guarantees callers that all fields are safe to access without null checks.

Chisels and hand tools neatly arranged and strapped to a workshop wall
Photo by Ahmet Kurt on Unsplash

How Do Pick and Omit Shape Interfaces?

Pick<T, K> selects a subset of keys. Omit<T, K> removes specific keys. Both create new types derived from an existing one. Pick and Omit are the two utility types you reach for whenever a component or endpoint needs a narrower view of a wider model rather than the whole thing. I've shipped API response types built almost entirely from Omit<InternalRecord, 'passwordHash' | 'internalNotes'> patterns, three or four sensitive fields stripped, everything else passed through untouched, and that single line does the job that used to take a fifteen-field interface written by hand. The tradeoff between the two is mostly about how many fields you are naming: per the TypeScript documentation, both compile to the same kind of mapped type under the hood, so there is no runtime cost difference, the choice is purely about readability at the call site.

interface Product {
  id: string;
  name: string;
  price: number;
  stock: number;
  createdAt: Date;
  updatedAt: Date;
}

// Only expose what the UI needs
type ProductCard = Pick<Product, 'id' | 'name' | 'price'>;

// Strip internal timestamps from the public API response
type PublicProduct = Omit<Product, 'createdAt' | 'updatedAt'>;

Which should you use? Pick is better when the subset is small. Omit is better when you're removing one or two fields from a large interface. If you find yourself listing 8 fields in a Pick, switch to Omit.

Combining Pick and Partial for Form State

This pattern shows up in almost every real form codebase:

// Full user type
interface User {
  id: string;
  name: string;
  email: string;
  bio: string;
}

// Editable fields only, all optional for draft state
type ProfileDraft = Partial<Pick<User, 'name' | 'email' | 'bio'>>;

The type says exactly what it means: a draft profile edit where any combination of name, email, or bio can be present.

How Does Record Work?

Record<K, V> creates an object type where all keys are of type K and all values are of type V. It's the typed alternative to { [key: string]: SomeType }. Record is the utility type that pairs a key union with a value type and forces every member of that union to have a corresponding entry, which is exactly what an index signature cannot do on its own. The difference matters more than it looks: an index signature accepts any string key and silently returns undefined for one that is missing, while Record<HttpStatus, string> fails to compile the moment one of the seven status codes lacks a message. I have used this to catch missing i18n translation keys before merge, seven locales, one union of message IDs, and TypeScript refuses to build if any locale file is missing an entry. That is a compile-time guarantee an index signature simply cannot offer.

type HttpStatus = 200 | 201 | 400 | 401 | 403 | 404 | 500;

const statusMessages: Record<HttpStatus, string> = {
  200: 'OK',
  201: 'Created',
  400: 'Bad Request',
  401: 'Unauthorized',
  403: 'Forbidden',
  404: 'Not Found',
  500: 'Internal Server Error',
};

// TypeScript ensures every HttpStatus key is present
// Adding a new status to HttpStatus forces you to add a message too

Using a union as the key type is where Record really shines. TypeScript will flag missing keys at compile time.

A pegboard wall packed with tools above a cluttered workbench
Photo by camera obscura on Unsplash

What Do Exclude and Extract Do?

These work on union types, not object types. That distinction trips people up, mostly because Pick and Omit operate on object keys while Exclude and Extract operate on the members of a union, and the syntax looks similar enough that it's easy to reach for the wrong one. Exclude<T, U> is the utility type that removes any member of union T that also appears in U, leaving the rest untouched. TypeScript 2.8 shipped both Exclude and Extract in the same release that introduced conditional types, since both are implemented as conditional types under the hood, per the TypeScript release notes for that version. Two common uses cover most real code: filtering null and undefined out of a nullable union, and pulling a single variant out of a discriminated union without writing a type guard by hand.

type Status = 'active' | 'inactive' | 'pending' | 'banned';

// Remove specific members from the union
type ActiveStatus = Exclude<Status, 'banned' | 'inactive'>;
// Result: 'active' | 'pending'

// Keep only the members that match
type ClosedStatus = Extract<Status, 'inactive' | 'banned'>;
// Result: 'inactive' | 'banned'

A practical use: filtering out null and undefined from a union.

type MaybeString = string | null | undefined;

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

TypeScript 2.8 actually uses Exclude to implement NonNullable<T> internally. So NonNullable<T> is just Exclude<T, null | undefined> under the hood.

Which Utility Types Should I Learn First?

Start with Partial and Pick. You'll use them on day one. Then add Omit and Record. Save Exclude and Extract for when you start working with complex union manipulation. If you're onboarding a junior developer onto a TypeScript codebase, this order roughly matches how often each utility type actually appears in a typical pull request queue: Partial and Pick show up almost daily, Omit and Record a few times a week, Exclude and Extract closer to once a month. Twenty-plus utility types exist in current TypeScript releases, but according to the TypeScript Handbook's own utility types page, the seven documented at the top of that reference page are the ones the compiler team considers foundational, everything past that list, ConstructorParameters, InstanceType, Awaited, exists for narrower metaprogramming cases you'll meet only when you need them.

Utility typeWhat it doesReach for it when
Partial<T>Makes every property optionalUpdate payloads, form drafts, config overrides
Required<T>Makes every property requiredValidating a config object after defaults are merged in
Pick<T, K>Keeps only the named keysNarrowing a wide model down to what one component needs
Omit<T, K>Drops the named keysStripping id or createdAt before a create call
Record<K, T>Maps a key union onto a value typeLookup tables and dictionaries keyed by a union
Exclude<T, U>Removes matching members from a unionFiltering null and undefined out of a union
Extract<T, U>Keeps only matching union membersPulling one variant out of a discriminated union

Don't try to memorize the full list. TypeScript 5.x ships around 20 utility types. Most of them are useful occasionally. The seven covered here cover 90% of real cases.

  • TypeScript Generics: A Practical Guide for React Developers - utility types are built from generic mapped types; understanding generics helps you build your own utilities
  • TypeScript 7 (Project Corsa) guide - the native Go compiler type-checks deeply nested utility type chains 10x faster; the overview explains what changed at the July 2026 GA
  • TypeScript 7 migration - the step-by-step upgrade, worth reading before you lean into Pick, Omit, and conditional types at scale
  • Node.js 20 to 24 migration - @types/node ships updated utility-compatible types for new Node.js globals like ReadableStream and fetch; upgrading Node changes which types you need
  • React Hooks pitfalls - use Partial<T> and Pick<T, K> to type hook return values and state objects cleanly

Frequently Asked Questions

What is the difference between Pick and Omit?
Pick<T, K> creates a new type with only the keys you list. Omit<T, K> creates a new type with everything except the keys you list. Use Pick when you want a small subset; use Omit when it's easier to name what to remove.
When should I use Partial vs Required?
Use Partial<T> when building update payloads or form drafts where not every field is present. Use Required<T> when you need to enforce that every optional field is provided, such as after validation.
What is the difference between Exclude and Extract?
Exclude<T, U> removes members of U from a union T. Extract<T, U> keeps only the members that exist in both T and U. They are inverses of each other.
Can I combine utility types?
Yes. You can compose them freely: Partial<Pick<User, 'name' | 'email'>> creates a type with optional name and email fields only. Nesting utility types is a normal TypeScript pattern.