BytePane

TypeScript Utility Types Comparator

Side-by-side visual reference for all 8 core TypeScript utility types — Omit, Pick, Partial, Required, Readonly, Record, Exclude, Extract. Each entry shows the input type, the resulting output type, when to use it, and real codebase patterns. Use this as a quick reference or as a decision tree before reaching for a utility.

Quick decision rule:
  • • Object property removal/selection → Omit / Pick
  • • Optional ↔ Required toggling → Partial / Required
  • • Immutability → Readonly
  • • Dictionary/map with known keys → Record
  • • Union member filtering → Exclude / Extract

Decision tree — Omit vs the alternatives

QuestionOption AOption B
Want fewer properties from an object type?Use Omit (when removing only a few)Use Pick (when keeping only a few)
Want fields optional vs required?Use Partial (all optional)Use Required (all required)
Want immutability or mutable?Use Readonly (no reassignment)Default mutable (no wrapper)
Working with object properties or union members?Object: use Omit/PickUnion: use Exclude/Extract
Need a dictionary/map structure?Use Record<Keys, Type>Avoid index signatures when keys are known

Side-by-side comparator (8 utility types)

Omit

Omit<Type, Keys>Object typeIntroduced: TS 3.5 (June 2019)

Construct a new type by REMOVING specified properties from an object type. Internally: Pick<T, Exclude<keyof T, K>>.

INPUT
type User = { id: string; name: string; password: string; email: string }
OUTPUT
type PublicUser = Omit<User, "password" | "email">
// = { id: string; name: string }
When to use

When you want most properties of a type but need to STRIP a few. Common: stripping server-controlled fields (id, createdAt, updatedAt) from API request payloads, or hiding sensitive fields from public API responses.

Common patterns (3)
  • API request from entity: type CreateUserRequest = Omit<User, "id" | "createdAt" | "updatedAt">
  • Public-facing type: type PublicUser = Omit<User, "password" | "email">
  • Component props extending DOM: type ButtonProps = Omit<React.ButtonHTMLAttributes<HTMLButtonElement>, "type"> & { type: "primary" | "secondary" }

Pick

Pick<Type, Keys>Object typeIntroduced: TS 2.1 (December 2016)

Construct a new type by SELECTING only the specified properties from an object type — the inverse of Omit.

INPUT
type User = { id: string; name: string; password: string; email: string }
OUTPUT
type UserSummary = Pick<User, "id" | "name">
// = { id: string; name: string }
When to use

When you want a SUBSET — listing the few properties you want from a larger type. Decision rule: Pick when you keep few, Omit when you keep many. Goal is the shorter, more readable type.

Common patterns (3)
  • Summary view: type UserSummary = Pick<User, "id" | "name" | "avatar">
  • Form data subset: type LoginFormData = Pick<User, "email" | "password">
  • Composable with Omit: type CleanUser = Pick<Omit<User, "password">, "id" | "name" | "email">

Partial

Partial<Type>Mapped typeIntroduced: TS 2.1 (December 2016)

Make ALL properties of a type optional by adding ? modifier to each.

INPUT
type User = { id: string; name: string; email: string }
OUTPUT
type UserUpdate = Partial<User>
// = { id?: string; name?: string; email?: string }
When to use

For PATCH update payloads where any subset of fields can be updated. Also for default options merging, configuration objects, and partial state in React useReducer reducers.

Common patterns (3)
  • Update payload: type UpdateUserRequest = Partial<Omit<User, "id">> & { id: string }
  • Config with defaults: function configure(opts: Partial<Config>) { return { ...DEFAULTS, ...opts } }
  • Selective Partial: Partial<Pick<User, "name" | "email">> — only some fields optional

Required

Required<Type>Mapped typeIntroduced: TS 2.8 (March 2018)

Make ALL properties of a type required (removes ? modifier from each). Inverse of Partial.

INPUT
type Config = { theme?: string; lang?: string; debug?: boolean }
OUTPUT
type FullConfig = Required<Config>
// = { theme: string; lang: string; debug: boolean }
When to use

When you have a Partial-like type and need to assert all fields exist after a merge or validation step. Also for narrowing types after default-filling logic.

Common patterns (3)
  • Post-merge type: const fullConfig: Required<Config> = { ...DEFAULTS, ...userConfig }
  • Validated form: type ValidForm = Required<RawFormData>
  • Server response after validation: Required<Pick<UserResponse, "id" | "email">>

Readonly

Readonly<Type>Mapped typeIntroduced: TS 2.1 (December 2016)

Mark all properties of a type as readonly — prevents reassignment after creation.

INPUT
type Point = { x: number; y: number }
OUTPUT
type FrozenPoint = Readonly<Point>
// const p: FrozenPoint = { x: 1, y: 2 }; p.x = 5; // ERROR
When to use

For immutable data structures, configuration constants, Redux state, props that should not be mutated, and pure-function inputs. Important: Readonly is shallow — nested objects are still mutable. Use DeepReadonly for full immutability.

Common patterns (3)
  • Immutable config: const CONFIG: Readonly<AppConfig> = { ... }
  • Redux state slice: type State = Readonly<{ users: User[]; loading: boolean }>
  • React props: function Card(props: Readonly<Props>) { ... }

Record

Record<Keys, Type>Mapped typeIntroduced: TS 2.1 (December 2016)

Construct a type with a set of properties Keys of type Type — useful for dictionary-like structures.

INPUT
type StatusCode = 200 | 404 | 500
OUTPUT
type StatusMessages = Record<StatusCode, string>
// = { 200: string; 404: string; 500: string }
When to use

For maps where you have a known finite key set and uniform value type. Common: lookup tables, action handler maps, theme color tables, status-to-message mappings.

Common patterns (3)
  • Lookup table: type StatusMessages = Record<StatusCode, string>
  • Action handler map: const handlers: Record<ActionType, Handler> = { ... }
  • Theme: type Colors = Record<"primary" | "accent" | "warning", string>

Exclude

Exclude<UnionType, ExcludedMembers>Union typeIntroduced: TS 2.8 (March 2018)

Remove members from a UNION type that are assignable to the excluded members. Operates on UNIONS, not object properties.

INPUT
type Status = "loading" | "success" | "error" | "idle"
OUTPUT
type Resolved = Exclude<Status, "loading" | "idle">
// = "success" | "error"
When to use

Narrowing union types — removing impossible states, filtering allowed values, deriving subtypes. Different from Omit (which works on OBJECT properties). Internally used by Omit.

Common patterns (3)
  • Resolved status narrowing: type Resolved = Exclude<Status, "loading" | "idle">
  • String literal filtering: type NonHTTPMethod = Exclude<keyof typeof methods, "GET" | "POST">
  • Discriminated union narrowing: Exclude<Action, { type: "RESET" }>

Extract

Extract<UnionType, IncludedMembers>Union typeIntroduced: TS 2.8 (March 2018)

Extract members from a UNION type that are assignable to the included members — inverse of Exclude.

INPUT
type Status = "loading" | "success" | "error" | "idle"
OUTPUT
type Pending = Extract<Status, "loading" | "idle">
// = "loading" | "idle"
When to use

Selecting specific members from a union — filtering for allowed types, deriving narrow subtypes, exposing only certain variants of a discriminated union to consumers.

Common patterns (3)
  • Narrow allowed status: type Pending = Extract<Status, "loading" | "idle">
  • Function value extraction: type StringMethods = Extract<keyof Array<any>, string>
  • Discriminated extract: type ResetAction = Extract<Action, { type: "RESET" }>

Composability — chaining utility types

Real-world TypeScript codebases compose 2–5 utility types per non-trivial type definition. Examples below show common production chains.

PATCH update payload — id required, rest optional
type UpdateUserRequest = Partial<Omit<User, "id">> & { id: string }
Immutable public subset
type PublicConfig = Readonly<Pick<AppConfig, "version" | "apiUrl" | "publicKeys">>
Action handler dictionary with discriminated unions
type ActionHandlers = Record<Action["type"], (state: State, action: Extract<Action, { type: K }>) => State>
Distributive Omit (preserves union narrowing)
type DistributiveOmit<T, K extends keyof any> = T extends any ? Omit<T, K> : never
type WithoutId = DistributiveOmit<Animal | Vehicle, "id">

Frequently Asked Questions

What is the difference between Omit and Pick in TypeScript?

Pick<T, K> SELECTS the specified keys from a type. Omit<T, K> REMOVES the specified keys. They are inverses. Use Pick when you keep few properties, Omit when you keep many — choose by readability. Internally Omit is implemented as Pick<T, Exclude<keyof T, K>>.

When do I use Partial vs Required in TypeScript?

Partial<T> makes ALL properties optional (adds ? modifier). Use for PATCH update payloads, config objects with defaults, partial state in reducers. Required<T> makes ALL properties required (removes ? modifier). Use after merging defaults to assert all fields exist, or after validation. They are inverses operating on the optional/required modifier.

What is the difference between Omit and Exclude in TypeScript?

Omit operates on OBJECT types (removes properties). Exclude operates on UNION types (removes members). Omit<{a:1; b:2}, "a"> = {b:2}. Exclude<"a"|"b"|"c", "a"> = "b"|"c". They are different abstraction levels but compose: Omit is built using Exclude internally (Pick<T, Exclude<keyof T, K>>).

Is TypeScript Readonly deep or shallow?

Readonly<T> is SHALLOW — only top-level properties are marked readonly. Nested objects remain mutable. To get full immutability, define a DeepReadonly helper: type DeepReadonly<T> = { readonly [K in keyof T]: T[K] extends object ? DeepReadonly<T[K]> : T[K] }. Or use libraries like ts-toolbelt for production-grade deep immutability types.

When should I use Record<K, V> vs an index signature?

Record<K, V> when keys are a KNOWN FINITE set (string literals, enum values, narrow union). Index signatures { [key: string]: V } when keys are OPEN/UNKNOWN. Record gives type-safety on known keys (typo errors caught), index signatures permit any string. Combining them: Record<KnownKeys, V> & Partial<Record<string, V>> for "known keys plus dynamic extras".

How do TypeScript utility types compose?

They compose orthogonally — combine 3-5 in any non-trivial codebase. Common compositions: Partial<Omit<User, "id">> (update payload, all fields optional except id is removed), Readonly<Pick<Config, "apiKey" | "version">> (immutable subset), Record<HttpMethod, (req: Request) => Response> (handler map). Order matters: Partial<Omit<>> ≠ Omit<Partial<>> in some cases. Most production type definitions chain 2-4 utility types.

Does TypeScript Omit work on union types?

Omit is NON-DISTRIBUTIVE by default — it operates on a union as a whole, not on each member. type Animal = {name: string; legs: 4} | {name: string; wings: 2}; Omit<Animal, "name"> works because both members have name. To make Omit distributive: type DistributiveOmit<T, K extends keyof any> = T extends any ? Omit<T, K> : never. This is essential for polymorphic component prop typing in libraries like Material UI.

What TypeScript version introduced Omit?

Omit was added in TypeScript 3.5 (June 2019). Pick, Partial, Readonly, and Record were earlier (TS 2.1 December 2016). Required, Exclude, Extract arrived in TS 2.8 (March 2018). For projects on TS < 3.5, Omit can be polyfilled: type Omit<T, K extends keyof any> = Pick<T, Exclude<keyof T, K>>.

Reference compiled from TypeScript handbook (typescriptlang.org/docs/handbook/utility-types.html), TS source code in lib/lib.es5.d.ts, and production codebase patterns. Last updated April 2026, TypeScript 5.7+.