tryjs.app

TypeScript Utility Types — Partial, Pick, Omit & Record

Learn TypeScript utility types with runnable examples. Partial, Required, Pick, Omit, Record, Readonly, and more built-in mapped types.

Utility Types

Built-in mapped and conditional types

typescriptTypeScript Essentials
interface User {
  id: number;
  name: string;
  email: string;
  role: "admin" | "user";
}

// Partial: all fields optional — good for update payloads
type UpdateData = Partial<User>;
const change: UpdateData = { name: "Alice" };
console.log(change);

// Pick: select specific fields
type UserPreview = Pick<User, "id" | "name">;
const preview: UserPreview = { id: 1, name: "Alice" };
console.log(preview);

// Omit: exclude specific fields
type PublicUser = Omit<User, "email">;
const pub: PublicUser = { id: 2, name: "Bob", role: "user" };
console.log(pub);

// Record: map keys to values
type RoleCount = Record<User["role"], number>;
const counts: RoleCount = { admin: 3, user: 42 };
console.log(counts);

// Readonly: immutable snapshot (shallow)
type Frozen = Readonly<User>;
const user: Frozen = { id: 3, name: "Carol", email: "c@x.io", role: "admin" };
// user.name = "Dana"; // compile error

Overview

TypeScript ships with a set of utility types that transform existing types in common ways: making every field optional, picking a subset, removing fields, creating a key-value map from a union. They're the building blocks of type-level programming in TypeScript and replace most hand-written mapped types. Knowing the main six — Partial, Required, Pick, Omit, Record, Readonly — covers 90% of real-world use cases.

How It Works

Partial<T> — all fields optional

Makes every property of T optional. Perfect for update-style functions: function update(user: User, changes: Partial<User>).

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

Pick selects a subset of keys; Omit removes them. They're complementary: Pick<User, 'id' | 'name'> gives you just those two fields, Omit<User, 'password'> gives everything except password.

Record<K, V> — key-value map

Record<'admin' | 'user', number> creates { admin: number; user: number }. The first parameter is the set of keys, the second is the value type.

Readonly<T> and ReturnType<F>

Readonly marks every property as read-only at the type level. ReturnType<typeof fn> extracts the return type of a function type — handy for inferring types from existing code.

Common Mistakes

  • Reaching for Partial<T> when you only need a subset — Pick is more precise and documents intent.
  • Using Omit with a typo'd key — it silently succeeds in TypeScript 4.5+. Double-check spelling.
  • Forgetting that Readonly is shallow — nested objects are still mutable.
  • Combining utility types without intermediate aliases, ending up with unreadable one-liners.

When to Use It

Reach for these whenever you're deriving one type from another — update payloads (Partial), preview types (Pick), stripping secrets (Omit), lookup maps (Record), immutable snapshots (Readonly). They keep types in sync when the source type changes.

Frequently Asked Questions

What is Partial<T> in TypeScript?

Partial<T> is a utility type that makes every property of T optional. It's perfect for update functions and patch payloads where the caller may supply any subset of the fields.

What is the difference between Pick and Omit?

They're complementary. Pick<T, K> creates a type with only the listed keys. Omit<T, K> creates a type with the listed keys removed. Use whichever is shorter: Pick when you want a few keys, Omit when you want most of them.

What is Record<K, V>?

Record<K, V> is a type with keys of type K and values of type V. Record<'admin' | 'user', number> is shorthand for { admin: number; user: number }. It's the canonical way to build lookup maps.

Is Readonly deep or shallow?

Shallow — only the top-level properties are marked read-only. Nested objects can still be mutated. For deep immutability you need a recursive helper type like DeepReadonly<T>.