| |

TypeScript 34 ๐Ÿ”ท Utility Types โ€” Partial, Required, Readonly, Pick, Omit

TypeScript ships a set of utility types โ€” pre-built type transformations that solve common problems. The five in this chapter are the ones you’ll use daily: Partial<T> makes every property optional, Required<T> makes them required, Readonly<T> makes them read-only, Pick<T, K> selects a subset of properties, and Omit<T, K> removes a subset. Each is a small mapped type that reshapes an object type. Once you can read them, you can read the rest of the standard library โ€” because they’re all the same pattern: iterate the keys, apply a rule.

Key point: These five are mapped types at heart. Partial<T> is { [K in keyof T]?: T[K] }, Readonly<T> is { readonly [K in keyof T]: T[K] }, and so on. Understanding one means understanding all. They’re the vocabulary of TypeScript’s type transformations โ€” used everywhere in libraries, API clients, form handlers, and state management. Learn them cold, and you’ll recognize them in every codebase.


Partial<T> โ€” make everything optional

Partial<T> makes every property of T optional.

interface User {
  id: number;
  name: string;
  email: string;
}

type PartialUser = Partial<User>;
// {
//   id?: number;
//   name?: string;
//   email?: string;
// }

Each property gets ? and its type stays the same.

Implementation:

type Partial<T> = {
  [K in keyof T]?: T[K];
};

Iterates every key, adds ?, keeps the value type.

Why it’s useful: Partial is the type for updates. A PATCH request, a form’s draft state, a patch object โ€” all are partial versions of the full type.

function updateUser(id: number, changes: Partial<User>): User {
  return { ...getUser(id), ...changes };
}

updateUser(1, { name: 'Alice' });          // โœ… only name
updateUser(1, { email: 'a@b.c' });         // โœ… only email
updateUser(1, { name: 'Alice', age: 30 }); // โŒ age doesn't exist

Every field is optional, but no new fields can appear.

Nested objects are not affected: Partial<T> is shallow. Nested objects keep their required properties.

interface Order {
  id: string;
  customer: { id: number; name: string };
}

type PartialOrder = Partial<Order>;
// {
//   id?: string;
//   customer?: { id: number; name: string };  โ† inner object unchanged
// }

The customer property can be missing, but if it’s present, both id and name are required.

Why shallow matters: Partial<T> is one level. For deep optionality, you need a recursive DeepPartial<T>. The standard type is shallow because deep recursion is expensive and often not what you want.

When to use Partial<T>:

  • Function parameters with optional overrides
  • Update payloads
  • Draft states
  • Defaults merged with incoming config
  • Anywhere an object might be incomplete

Why “Partial” is the right name: It says the type is a partial version of the original โ€” some (or all) properties may be missing. The other utilities have names that describe what they do too: Required makes everything required, Pick picks properties, Omit omits them. The naming is direct.


Required<T> โ€” make everything required

Required<T> makes every property of T required โ€” the inverse of Partial.

interface Config {
  host?: string;
  port?: number;
  ssl?: boolean;
}

type FullConfig = Required<Config>;
// {
//   host: string;
//   port: number;
//   ssl: boolean;
// }

Every optional property becomes required.

Implementation:

type Required<T> = {
  [K in keyof T]-?: T[K];
};

The -? removes the optional modifier.

Why it’s useful: When you need to promise that a config is fully populated after defaults are applied.

function applyDefaults(config: Partial<Config>): Required<Config> {
  return {
    host: 'localhost',
    port: 8080,
    ssl: false,
    ...config
  };
}

const full = applyDefaults({ port: 3000 });
// full is Required<Config>
// host, port, ssl are all present

The return type guarantees every field is set. Consumers can access any property without optional chaining.

Also shallow: Required<T> affects only the top level. Nested optional properties stay optional.

Why -? instead of a new modifier: The - prefix removes a modifier rather than adding one. TypeScript uses it for both -? (remove optional) and -readonly (remove readonly). It’s a small syntax that makes “the inverse of this transformation” expressible.

When to use Required<T>:

  • After applying defaults
  • When validating a partial config
  • To assert that a promise has been fulfilled
  • Anywhere “all fields are present” is a guarantee

Why Required<T> is less common than Partial<T>: Partial types are everywhere โ€” every update payload, every config override. Required types are the assertion that the partial has been completed. You often transform to Required<T> internally, but expose Partial<T> to callers.


Readonly<T> โ€” make everything read-only

Readonly<T> makes every property of T immutable.

interface User {
  id: number;
  name: string;
}

type ImmutableUser = Readonly<User>;
// {
//   readonly id: number;
//   readonly name: string;
// }

const user: ImmutableUser = { id: 1, name: 'Alice' };
user.name = 'Bob';   // โŒ read-only

Assignment after creation is a compile error.

Implementation:

type Readonly<T> = {
  readonly [K in keyof T]: T[K];
};

Adds the readonly modifier to every property.

Why it’s useful: Immutability is a contract. A Readonly<User> promises consumers won’t be modified โ€” and the compiler enforces it.

function processUser(user: Readonly<User>): void {
  // user.name = 'x';  // โŒ can't modify
  console.log(user.name);
}

The function signature says “I won’t change this” โ€” and the compiler checks.

Also shallow: Readonly<T> affects only the top level. A readonly object with an array property still has a mutable array.

interface Data {
  items: string[];
}

const d: Readonly<Data> = { items: ['a'] };
d.items = ['b'];       // โŒ can't reassign
d.items.push('c');     // โœ… can mutate

The items property can’t be reassigned, but the array’s contents can be mutated. For deep immutability, use a recursive DeepReadonly<T> or readonly T[].

Readonly<T> vs as const: Both create readonly types, but differently.

AspectReadonly<T>as const
Applied toTypeValue
ResultReadonly typeReadonly literal type
WideningPreservedPrevented
RuntimeNo effectNo effect

Readonly<T> takes an existing type and makes it readonly. as const takes a value and freezes its type.

When to use Readonly<T>:

  • Function parameters that shouldn’t be mutated
  • Configuration objects
  • Shared state
  • React props (conceptually)
  • Anywhere the caller shouldn’t modify

Why shallow immutability is the default: Deep immutability would require recursing into every object and array, which is expensive and produces complex types. Most of the time, you want to prevent reassigning the top-level properties but allow mutating nested structures. Shallow Readonly<T> gives that. For the rest, reach for readonly T[] or a custom DeepReadonly<T>.


Pick<T, K> โ€” select properties

Pick<T, K> creates a new type with only the properties in K.

interface User {
  id: number;
  name: string;
  email: string;
  createdAt: Date;
}

type UserPreview = Pick<User, 'id' | 'name'>;
// {
//   id: number;
//   name: string;
// }

UserPreview has only id and name โ€” the ones listed.

Implementation:

type Pick<T, K extends keyof T> = {
  [P in K]: T[P];
};

Iterates over K (a union of keys) instead of keyof T. Each picked key gets its original type.

K must be a key of T: The constraint K extends keyof T prevents selecting nonexistent keys.

type Bad = Pick<User, 'missing'>;   // โŒ 'missing' not a key of User

The compiler catches typos and invalid keys.

Why it’s useful: Pick produces projections โ€” types with a subset of properties. It’s how you model “the fields I need for this view” or “the fields to display.”

type UserCard = Pick<User, 'id' | 'name' | 'email'>;
type UserAdmin = Pick<User, 'id' | 'name' | 'email' | 'createdAt'>;
type UserLink = Pick<User, 'id' | 'name'>;

Each is a specific view of User.

Keys can be dynamic: K can be a type parameter.

function pickFields<T, K extends keyof T>(obj: T, keys: K[]): Pick<T, K> {
  const result = {} as Pick<T, K>;
  for (const key of keys) {
    result[key] = obj[key];
  }
  return result;
}

const user: User = { id: 1, name: 'Alice', email: 'a@b.c', createdAt: new Date() };
const preview = pickFields(user, ['id', 'name']);
// preview: Pick<User, 'id' | 'name'>

The return type reflects exactly which keys were selected.

Pick preserves optionality: If a property is optional in T, it’s optional in Pick<T, K>.

interface User {
  id: number;
  nickname?: string;
}

type WithNick = Pick<User, 'nickname'>;
// { nickname?: string }

Why Pick matters: It’s how you derive specific views from a larger type without duplicating property declarations. Change User, and every Pick<User, ...> updates.

Why the constraint K extends keyof T: It guarantees the keys exist. Without it, you could pick any string, and the result would be a type with undeclared properties. The constraint makes Pick safe โ€” you can only select keys that are actually in the source.


Omit<T, K> โ€” remove properties

Omit<T, K> creates a new type with the properties in K removed.

interface User {
  id: number;
  name: string;
  email: string;
  createdAt: Date;
}

type NewUser = Omit<User, 'id' | 'createdAt'>;
// {
//   name: string;
//   email: string;
// }

NewUser has everything except id and createdAt.

Implementation:

type Omit<T, K extends keyof any> = Pick<T, Exclude<keyof T, K>>;

Omit is Pick with the complement of K. Exclude<keyof T, K> computes the keys to keep; Pick selects them.

Why this composition: It’s simpler than writing the map directly, and it shows how utility types build on each other. Omit = Pick + Exclude.

K need not be a key of T: Unlike Pick, Omit doesn’t require the removed keys to exist.

type A = Omit<User, 'id'>;              // โœ…
type B = Omit<User, 'missing'>;         // โœ… compiles โ€” no-op

If a key isn’t in T, Exclude ignores it. The result is the same as if it weren’t specified. That’s a subtle difference from Pick.

Why it’s useful: Omit produces types by subtraction. It’s how you model “everything except these fields.”

type UserUpdate = Omit<User, 'id' | 'createdAt'>;
// Everything except id and createdAt โ€” the updatable fields

type PublicUser = Omit<User, 'password'>;
// Everything except the sensitive field

Omit vs Pick:

NeedUse
Few propertiesPick<T, 'a' | 'b'>
Most propertiesOmit<T, 'c' | 'd'>
Everything except sensitiveOmit<T, 'password'>
Specific viewPick<T, 'a' | 'b'>

The rule of thumb: if you want most of the properties, use Omit with the exceptions. If you want a few, use Pick.

Why no keyof T constraint: Since Omit uses Exclude<keyof T, K>, the constraint would be redundant. Any string is accepted; non-matching ones have no effect. It’s a small looseness that makes Omit forgiving.

When to use Omit<T>:

  • Removing sensitive fields before exposing
  • Removing server-generated fields from input types
  • Deriving update types from entity types
  • Anywhere you want “everything except”

Why both Pick and Omit exist: They’re complementary. Pick says “I want these.” Omit says “I want everything except these.” Which reads better depends on how many fields you’re naming. With one or two exceptions, Omit is clearer. With a small subset, Pick is clearer.


A full example

Using all five utilities for a user API.

// ============================================
// DOMAIN TYPE
// ============================================

interface User {
  id: number;
  name: string;
  email: string;
  password: string;
  role: 'admin' | 'user' | 'guest';
  createdAt: Date;
  updatedAt: Date;
}

// ============================================
// VIEWS AND INPUTS
// ============================================

// Public view โ€” no password
type PublicUser = Omit<User, 'password'>;
// {
//   id: number;
//   name: string;
//   email: string;
//   role: 'admin' | 'user' | 'guest';
//   createdAt: Date;
//   updatedAt: Date;
// }

// Summary view โ€” only basic fields
type UserSummary = Pick<User, 'id' | 'name' | 'role'>;
// {
//   id: number;
//   name: string;
//   role: 'admin' | 'user' | 'guest';
// }

// Create input โ€” no id or timestamps
type CreateUserInput = Omit<User, 'id' | 'createdAt' | 'updatedAt'>;
// {
//   name: string;
//   email: string;
//   password: string;
//   role: 'admin' | 'user' | 'guest';
// }

// Update input โ€” partial, no immutable fields
type UpdateUserInput = Partial<Omit<User, 'id' | 'createdAt' | 'updatedAt'>>;
// {
//   name?: string;
//   email?: string;
//   password?: string;
//   role?: 'admin' | 'user' | 'guest';
// }

// Frozen config โ€” readonly
type FrozenUser = Readonly<User>;
// All properties readonly

// Fully required โ€” after applying defaults
type FullUser = Required<PublicUser>;

// ============================================
// API FUNCTIONS
// ============================================

function createUser(input: CreateUserInput): PublicUser {
  const user: User = {
    id: Math.floor(Math.random() * 1000),
    ...input,
    createdAt: new Date(),
    updatedAt: new Date()
  };
  return stripPassword(user);
}

function updateUser(id: number, input: UpdateUserInput): PublicUser {
  const user = findUser(id);
  const updated: User = {
    ...user,
    ...input,
    updatedAt: new Date()
  };
  return stripPassword(updated);
}

function getUserSummary(user: User): UserSummary {
  return {
    id: user.id,
    name: user.name,
    role: user.role
  };
}

function stripPassword(user: User): PublicUser {
  const { password, ...rest } = user;
  return rest;
}

function findUser(id: number): User {
  return {
    id,
    name: 'Alice',
    email: 'alice@example.com',
    password: 'hashed',
    role: 'user',
    createdAt: new Date(),
    updatedAt: new Date()
  };
}

// ============================================
// USAGE
// ============================================

const created = createUser({
  name: 'Bob',
  email: 'bob@example.com',
  password: 'secret',
  role: 'user'
});

// created.password is not accessible โ€” it was omitted
console.log(created.name, created.email);

const updated = updateUser(1, { name: 'Alice Smith' });
console.log(updated.name);

const summary = getUserSummary(findUser(1));
console.log(summary);

// Wrong field types fail
// createUser({ name: 'x', email: 'x', password: 'x', role: 'invalid' });  // โŒ
// updateUser(1, { id: 2 });  // โŒ id is omitted from update input

What this shows:

  • PublicUser โ€” Omit removes the sensitive field
  • UserSummary โ€” Pick selects the view fields
  • CreateUserInput โ€” Omit removes server-generated fields
  • UpdateUserInput โ€” Partial<Omit<...>> combines two utilities
  • FrozenUser โ€” Readonly prevents mutation
  • FullUser โ€” Required asserts completeness

Each type is derived from User. Change User, and every view updates.

Why this shape: It’s how real APIs are typed. The domain type is the source of truth. Views, inputs, and assertions are derived via utilities. No duplication โ€” one place to change, everything follows.


Complete Example Session

# ============================================
# PART 1: PARTIAL
# ============================================

cat > partial.ts << 'EOF'
interface User {
  id: number;
  name: string;
  email: string;
}

type PartialUser = Partial<User>;

const a: PartialUser = {};
const b: PartialUser = { name: 'Alice' };
const c: PartialUser = { id: 1, name: 'Alice', email: 'a@b.c' };

console.log(a, b, c);
EOF

npx tsc --noEmit partial.ts
# (no errors)

# ============================================
# PART 2: REQUIRED
# ============================================

cat > required.ts << 'EOF'
interface Config {
  host?: string;
  port?: number;
  ssl?: boolean;
}

type FullConfig = Required<Config>;

const a: FullConfig = { host: 'localhost', port: 8080, ssl: false };
// const b: FullConfig = { host: 'localhost' };  // โŒ missing port

console.log(a);
EOF

npx tsc --noEmit required.ts
# (no errors)

# ============================================
# PART 3: READONLY
# ============================================

cat > readonly.ts << 'EOF'
interface User {
  id: number;
  name: string;
}

type ImmutableUser = Readonly<User>;

const user: ImmutableUser = { id: 1, name: 'Alice' };
// user.name = 'Bob';  // โŒ readonly

console.log(user.name);
EOF

npx tsc --noEmit readonly.ts
# (no errors)

# ============================================
# PART 4: PICK
# ============================================

cat > pick.ts << 'EOF'
interface User {
  id: number;
  name: string;
  email: string;
  createdAt: Date;
}

type UserPreview = Pick<User, 'id' | 'name'>;

const preview: UserPreview = { id: 1, name: 'Alice' };
// const bad: UserPreview = { id: 1, name: 'Alice', email: 'a@b.c' };  // โŒ

console.log(preview);
EOF

npx tsc --noEmit pick.ts
# (no errors)

# ============================================
# PART 5: OMIT
# ============================================

cat > omit.ts << 'EOF'
interface User {
  id: number;
  name: string;
  email: string;
  password: string;
  createdAt: Date;
}

type PublicUser = Omit<User, 'password'>;
type CreateInput = Omit<User, 'id' | 'createdAt'>;

const pub: PublicUser = {
  id: 1,
  name: 'Alice',
  email: 'a@b.c',
  createdAt: new Date()
};

const input: CreateInput = {
  name: 'Bob',
  email: 'b@c.d',
  password: 'secret'
};

console.log(pub, input);
EOF

npx tsc --noEmit omit.ts
# (no errors)

# ============================================
# PART 6: COMBINING
# ============================================

cat > combined.ts << 'EOF'
interface User {
  id: number;
  name: string;
  email: string;
  password: string;
  createdAt: Date;
  updatedAt: Date;
}

// Update input: partial, no immutable fields
type UpdateInput = Partial<Omit<User, 'id' | 'createdAt' | 'updatedAt'>>;
// {
//   name?: string;
//   email?: string;
//   password?: string;
// }

const update: UpdateInput = { name: 'Alice' };
const update2: UpdateInput = {};
// const bad: UpdateInput = { id: 1 };  // โŒ id not allowed

console.log(update, update2);
EOF

npx tsc --noEmit combined.ts
# (no errors)

# ============================================
# PART 7: VALIDATION ERRORS
# ============================================

cat > errors.ts << 'EOF'
interface User {
  id: number;
  name: string;
  email: string;
}

// โŒ Pick with invalid key
type Bad1 = Pick<User, 'missing'>;

// โŒ Partial expects object
type Bad2 = Partial<string>;
EOF

npx tsc --noEmit errors.ts
# [ errors.ts:8:18 - Type '"missing"' does not satisfy the constraint 'keyof User'. ]
# [ errors.ts:11:18 - Type 'string' does not satisfy the constraint 'object'. ]

rm errors.ts

# ============================================
# PART 8: COMPILE AND RUN
# ============================================

npx tsc partial.ts required.ts readonly.ts pick.ts omit.ts combined.ts
node partial.js
# [ {} { name: 'Alice' } { id: 1, name: 'Alice', email: 'a@b.c' } ]

node required.js
# [ { host: 'localhost', port: 8080, ssl: false } ]

node readonly.js
# [ Alice ]

node pick.js
# [ { id: 1, name: 'Alice' } ]

node omit.js
# [ { id: 1, name: 'Alice', email: 'a@b.c', createdAt: ... } { name: 'Bob', email: 'b@c.d', password: 'secret' } ]

node combined.js
# [ { name: 'Alice' } {} ]

Quick Reference

The Five Utilities

UtilityEffect
Partial<T>Every property optional
Required<T>Every property required
Readonly<T>Every property readonly
Pick<T, K>Only properties in K
Omit<T, K>Properties not in K

Implementations

UtilityDefinition
Partial<T>{ [K in keyof T]?: T[K] }
Required<T>{ [K in keyof T]-?: T[K] }
Readonly<T>{ readonly [K in keyof T]: T[K] }
Pick<T, K>{ [P in K]: T[P] }
Omit<T, K>Pick<T, Exclude<keyof T, K>>

Constraint on K

UtilityConstraint
Pick<T, K>K extends keyof T
Omit<T, K>K extends keyof any (loose)

Shallow vs Deep

UtilityDepth
Partial<T>Shallow
Required<T>Shallow
Readonly<T>Shallow
Pick<T, K>One level
Omit<T, K>One level

When to Use Each

NeedUtility
Update payloadPartial<T>
Assert completeRequired<T>
Freeze for readingReadonly<T>
Few fieldsPick<T, 'a' | 'b'>
Most fieldsOmit<T, 'x' | 'y'>
Remove sensitiveOmit<T, 'password'>
Apply defaultsRequired<Partial<T>>

Pick vs Omit

AspectPickOmit
ListsWhat to includeWhat to exclude
K constraintkeyof TAny string
Missing keyErrorNo-op
Best whenFew fieldsMany fields

Combined Patterns

PatternResult
Partial<Omit<T, 'id'>>Update input
Required<Partial<T>>Undo partial
Readonly<Pick<T, 'id'>>Readonly subset
Omit<Partial<T>, 'id'>Partial without id
Pick<T, keyof T>Identity
Omit<T, never>Identity

Common Recipes

RecipeType
Create inputOmit<T, 'id' | 'createdAt'>
Update inputPartial<Omit<T, 'id' | 'createdAt'>>
Public viewOmit<T, 'password'>
Summary viewPick<T, 'id' | 'name'>
Immutable entityReadonly<T>
Complete after defaultsRequired<Config>

Optional and Readonly Modifiers

ModifierApplied
?Partial
-?Required
readonlyReadonly
(none)Preserved

Distributing Over Unions

TypeBehavior
Partial<A | B>Error (union not object)
Partial<A> | Partial<B>Each is partial
Pick<A | B, K>Shared keys only
Omit<A | B, K>Shared keys only

Error Cases

ErrorCause
Type X does not satisfy constraint 'keyof T'Pick with invalid key
Type X is not assignable to 'object'Applied to primitive
Property does not existMissing required property
Cannot assign to readonlyMutation attempt

Interaction with Signals

TypeNotes
Partial<T>Works with signal values
Readonly<T>Prevents signal.set on readonly signal
Pick<T, K>Selects signal keys

Performance Notes

AspectDetail
Compile-timeShallow is fast
Deep recursionSlows compilation
OmitUses Exclude internally

Best Practices

โœ… Do This:

// Use Partial for update payloads
function update(id: number, patch: Partial<User>): void { }             // โœ…

// Use Readonly for parameters you won't mutate
function render(user: Readonly<User>): void { }                         // โœ…

// Use Pick for small views
type Preview = Pick<User, 'id' | 'name'>;                               // โœ…

// Use Omit for removing sensitive or generated fields
type Public = Omit<User, 'password'>;                                   // โœ…

// Combine Partial and Omit for updates
type UpdateInput = Partial<Omit<User, 'id' | 'createdAt'>>;             // โœ…

// Use Required after applying defaults
function withDefaults(c: Partial<Config>): Required<Config> { }         // โœ…

// Use Readonly on function parameters
function process(data: Readonly<Data>): void { }                        // โœ…

// Extract utilities from domain types
type UserSummary = Pick<User, 'id' | 'name' | 'role'>;                  // โœ…

// Use Omit<X, never> as identity โ€” rarely needed
type Same = Omit<User, never>;                                          // โœ…

โŒ Don’t Do This:

// Don't use Partial when properties are actually required
function save(user: Partial<User>): void {
  console.log(user.id.toString());  // โŒ id may be undefined
}

// Don't forget Readonly is shallow
const r: Readonly<Data> = { items: [] };
r.items.push('x');  // โš ๏ธ  mutates                             // โš ๏ธ

// Don't Pick invalid keys
type Bad = Pick<User, 'missing'>;  // โŒ                         // โŒ

// Don't expect Omit to error on missing keys
type Ok = Omit<User, 'missing'>;  // โš ๏ธ  no-op                  // โš ๏ธ

// Don't use utility types as replacements for interfaces
// Define the domain type first, then derive               // โš ๏ธ

// Don't over-nest utilities
type Complex = Partial<Required<Readonly<Pick<Omit<User, 'x'>, 'y'>>>>;
// Unreadable โ€” extract named types                        // โš ๏ธ

// Don't expect utility types to deep-apply
type Bad = Partial<Nested>;  // โš ๏ธ  inner objects unchanged    // โš ๏ธ

// Don't apply to non-object types
type Bad = Partial<string>;  // โŒ                             // โŒ

Common Pitfalls

PitfallProblemSolution
Expecting deep PartialNested objects unchangedUse recursive DeepPartial
Readonly is shallowArray/object contents mutableUse readonly T[]
Pick on unionOnly shared keysDistribute first
Omit typoSilent no-opCheck names
Partial on primitiveErrorOnly on objects
Required after PartialRestores all, not just someUse conditional
Pick with string KErrorUse literal keys
Over-nesting utilitiesUnreadableExtract named types
Readonly on functionNot applied to paramsAnnotate each
Confusing Partial and OmitWrong shapePartial makes optional; Omit removes

Real-World Examples

1. Partial for update payloads

type UserUpdate = Partial<User>;

2. Required after defaults

type FullConfig = Required<Config>;

3. Readonly for props

function render(props: Readonly<Props>): void { }

4. Pick for summary

type Summary = Pick<User, 'id' | 'name'>;

5. Omit for public view

type Public = Omit<User, 'password'>;

6. Create input

type CreateInput = Omit<User, 'id' | 'createdAt'>;

7. Update input

type UpdateInput = Partial<Omit<User, 'id'>>;

8. Identity type

type Same = Omit<User, never>;

9. Partial of a partial

type HalfUpdate = Partial<Omit<User, 'id' | 'createdAt'>>;

10. Required of a pick

type RequiredPreview = Required<Pick<User, 'id' | 'name'>>;

11. Readonly subset

type FrozenPreview = Readonly<Pick<User, 'id' | 'name'>>;

12. Pick all keys

type Clone = Pick<User, keyof User>;

13. Optional property then required

type FullUser = Required<Partial<User>>;
// Same as User

14. Nested partial

type DeepPartial<T> = {
  [K in keyof T]?: T[K] extends object ? DeepPartial<T[K]> : T[K];
};

15. Nested readonly

type DeepReadonly<T> = {
  readonly [K in keyof T]: T[K] extends object
    ? DeepReadonly<T[K]>
    : T[K];
};

16. Mutable

type Mutable<T> = {
  -readonly [K in keyof T]: T[K];
};

17. Concrete

type Concrete<T> = Required<{
  [K in keyof T]: NonNullable<T[K]>;
}>;

18. Projection

type Projection<T, K extends keyof T> = Readonly<Pick<T, K>>;

19. Patch type

type Patch<T> = Partial<Omit<T, 'id' | 'createdAt' | 'updatedAt'>>;

20. Diff patch with required id

type UpdateWithId<T> = Pick<T, 'id'> & Partial<Omit<T, 'id'>>;

Visual: The Five Utilities

โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”
โ”‚  Original: User                              โ”‚
โ”‚  {                                           โ”‚
โ”‚    id: number;                               โ”‚
โ”‚    name: string;                             โ”‚
โ”‚    email: string;                            โ”‚
โ”‚  }                                           โ”‚
โ”‚                                              โ”‚
โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜
       โ”‚
       โ”œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ–บ Partial<User>
       โ”‚           { id?, name?, email? }
       โ”‚
       โ”œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ–บ Required<User>
       โ”‚           { id, name, email }
       โ”‚
       โ”œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ–บ Readonly<User>
       โ”‚           { readonly id, name, email }
       โ”‚
       โ”œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ–บ Pick<User, 'id' | 'name'>
       โ”‚           { id, name }
       โ”‚
       โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ–บ Omit<User, 'email'>
                   { id, name }

Visual: Partial

โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”
โ”‚  interface User {                            โ”‚
โ”‚    id: number;                               โ”‚
โ”‚    name: string;                             โ”‚
โ”‚    email: string;                            โ”‚
โ”‚  }                                           โ”‚
โ”‚                                              โ”‚
โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜
                  โ”‚
                  โ”‚  Partial<User>
                  โ–ผ
โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”
โ”‚  {                                           โ”‚
โ”‚    id?: number;                              โ”‚
โ”‚    name?: string;                            โ”‚
โ”‚    email?: string;                           โ”‚
โ”‚  }                                           โ”‚
โ”‚                                              โ”‚
โ”‚  All valid:                                  โ”‚
โ”‚  {}                                          โ”‚
โ”‚  { id: 1 }                                   โ”‚
โ”‚  { name: 'Alice' }                           โ”‚
โ”‚  { id: 1, name: 'Alice', email: 'a@b.c' }    โ”‚
โ”‚                                              โ”‚
โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜

Visual: Required

โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”
โ”‚  interface Config {                          โ”‚
โ”‚    host?: string;                            โ”‚
โ”‚    port?: number;                            โ”‚
โ”‚  }                                           โ”‚
โ”‚                                              โ”‚
โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜
                  โ”‚
                  โ”‚  Required<Config>
                  โ–ผ
โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”
โ”‚  {                                           โ”‚
โ”‚    host: string;                             โ”‚
โ”‚    port: number;                             โ”‚
โ”‚  }                                           โ”‚
โ”‚                                              โ”‚
โ”‚  Must have both:                             โ”‚
โ”‚  { host: 'x', port: 1 }   โœ…                 โ”‚
โ”‚  { host: 'x' }            โŒ                 โ”‚
โ”‚                                              โ”‚
โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜

Visual: Readonly

โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”
โ”‚  interface User {                            โ”‚
โ”‚    id: number;                               โ”‚
โ”‚    name: string;                             โ”‚
โ”‚  }                                           โ”‚
โ”‚                                              โ”‚
โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜
                  โ”‚
                  โ”‚  Readonly<User>
                  โ–ผ
โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”
โ”‚  {                                           โ”‚
โ”‚    readonly id: number;                      โ”‚
โ”‚    readonly name: string;                    โ”‚
โ”‚  }                                           โ”‚
โ”‚                                              โ”‚
โ”‚  const u: Readonly<User> = { id: 1, name: 'A' };โ”‚
โ”‚  u.name = 'B';  โŒ                           โ”‚
โ”‚                                              โ”‚
โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜

Visual: Pick vs Omit

โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”
โ”‚  Original:                                   โ”‚
โ”‚  { a, b, c, d, e }                           โ”‚
โ”‚                                              โ”‚
โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜

โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”
โ”‚  Pick<T, 'a' | 'b'>                          โ”‚
โ”‚       โ”‚                                      โ”‚
โ”‚       โ–ผ                                      โ”‚
โ”‚  { a, b }                                    โ”‚
โ”‚                                              โ”‚
โ”‚  Lists what to include                       โ”‚
โ”‚                                              โ”‚
โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜

โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”
โ”‚  Omit<T, 'c' | 'd'>                          โ”‚
โ”‚       โ”‚                                      โ”‚
โ”‚       โ–ผ                                      โ”‚
โ”‚  { a, b, e }                                 โ”‚
โ”‚                                              โ”‚
โ”‚  Lists what to exclude                       โ”‚
โ”‚                                              โ”‚
โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜

Visual: Combining

โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”
โ”‚  interface User {                            โ”‚
โ”‚    id: number;                               โ”‚
โ”‚    name: string;                             โ”‚
โ”‚    password: string;                         โ”‚
โ”‚    createdAt: Date;                          โ”‚
โ”‚    updatedAt: Date;                          โ”‚
โ”‚  }                                           โ”‚
โ”‚                                              โ”‚
โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜
                  โ”‚
                  โ”‚  Omit<User, 'password'>
                  โ–ผ
โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”
โ”‚  PublicUser:                                 โ”‚
โ”‚  {                                           โ”‚
โ”‚    id: number;                               โ”‚
โ”‚    name: string;                             โ”‚
โ”‚    createdAt: Date;                          โ”‚
โ”‚    updatedAt: Date;                          โ”‚
โ”‚  }                                           โ”‚
โ”‚                                              โ”‚
โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜
                  โ”‚
                  โ”‚  Pick<PublicUser, 'id' | 'name'>
                  โ–ผ
โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”
โ”‚  Preview:                                    โ”‚
โ”‚  {                                           โ”‚
โ”‚    id: number;                               โ”‚
โ”‚    name: string;                             โ”‚
โ”‚  }                                           โ”‚
โ”‚                                              โ”‚
โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜

Visual: Update Input Pattern

โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”
โ”‚  Full entity:                                โ”‚
โ”‚  {                                           โ”‚
โ”‚    id: number;                               โ”‚
โ”‚    name: string;                             โ”‚
โ”‚    email: string;                            โ”‚
โ”‚    createdAt: Date;                          โ”‚
โ”‚    updatedAt: Date;                          โ”‚
โ”‚  }                                           โ”‚
โ”‚                                              โ”‚
โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜
                  โ”‚
                  โ”‚  Omit<User, 'id' | 'createdAt' | 'updatedAt'>
                  โ–ผ
โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”
โ”‚  Editable fields:                            โ”‚
โ”‚  {                                           โ”‚
โ”‚    name: string;                             โ”‚
โ”‚    email: string;                            โ”‚
โ”‚  }                                           โ”‚
โ”‚                                              โ”‚
โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜
                  โ”‚
                  โ”‚  Partial<...>
                  โ–ผ
โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”
โ”‚  Update input:                               โ”‚
โ”‚  {                                           โ”‚
โ”‚    name?: string;                            โ”‚
โ”‚    email?: string;                           โ”‚
โ”‚  }                                           โ”‚
โ”‚                                              โ”‚
โ”‚  All fields optional, no immutable fields    โ”‚
โ”‚                                              โ”‚
โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜

Visual: Shallow vs Deep

โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”
โ”‚  interface Order {                           โ”‚
โ”‚    id: string;                               โ”‚
โ”‚    customer: { id: number; name: string };   โ”‚
โ”‚  }                                           โ”‚
โ”‚                                              โ”‚
โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜

โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”
โ”‚  Partial<Order> โ€” shallow                    โ”‚
โ”‚                                              โ”‚
โ”‚  {                                           โ”‚
โ”‚    id?: string;                              โ”‚
โ”‚    customer?: { id: number; name: string };  โ”‚
โ”‚  }                                           โ”‚
โ”‚                                              โ”‚
โ”‚  customer is optional, but if present,       โ”‚
โ”‚  both id and name are required               โ”‚
โ”‚                                              โ”‚
โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜

โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”
โ”‚  DeepPartial<Order> โ€” recursive              โ”‚
โ”‚                                              โ”‚
โ”‚  {                                           โ”‚
โ”‚    id?: string;                              โ”‚
โ”‚    customer?: { id?: number; name?: string };โ”‚
โ”‚  }                                           โ”‚
โ”‚                                              โ”‚
โ”‚  Every level optional                        โ”‚
โ”‚                                              โ”‚
โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜

Visual: Decision Flow

โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”
โ”‚  Need to make properties optional?           โ”‚
โ”‚       โ””โ”€โ”€ Partial<T>                         โ”‚
โ”‚                                              โ”‚
โ”‚  Make properties required?                   โ”‚
โ”‚       โ””โ”€โ”€ Required<T>                        โ”‚
โ”‚                                              โ”‚
โ”‚  Make properties immutable?                  โ”‚
โ”‚       โ””โ”€โ”€ Readonly<T>                        โ”‚
โ”‚                                              โ”‚
โ”‚  Select specific properties?                 โ”‚
โ”‚       โ”œโ”€โ”€ Few fields โ”€โ”€โ–บ Pick<T, K>          โ”‚
โ”‚       โ””โ”€โ”€ Most fields โ”€โ”€โ–บ Omit<T, K>         โ”‚
โ”‚                                              โ”‚
โ”‚  Combine for derived types?                  โ”‚
โ”‚       โ””โ”€โ”€ Partial<Omit<T, K>> etc.           โ”‚
โ”‚                                              โ”‚
โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜

Visual: Common Combinations

โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”
โ”‚  Create input:                               โ”‚
โ”‚  Omit<T, 'id' | 'createdAt'>                 โ”‚
โ”‚                                              โ”‚
โ”‚  Update input:                               โ”‚
โ”‚  Partial<Omit<T, 'id' | 'createdAt'>>        โ”‚
โ”‚                                              โ”‚
โ”‚  Public view:                                โ”‚
โ”‚  Omit<T, 'password' | 'internalNotes'>       โ”‚
โ”‚                                              โ”‚
โ”‚  Summary:                                    โ”‚
โ”‚  Pick<T, 'id' | 'name'>                      โ”‚
โ”‚                                              โ”‚
โ”‚  Frozen:                                     โ”‚
โ”‚  Readonly<T>                                 โ”‚
โ”‚                                              โ”‚
โ”‚  Complete after defaults:                    โ”‚
โ”‚  Required<Config>                            โ”‚
โ”‚                                              โ”‚
โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜

Visual: The Omit Composition

โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”
โ”‚  Omit<T, K>                                  โ”‚
โ”‚  = Pick<T, Exclude<keyof T, K>>              โ”‚
โ”‚                                              โ”‚
โ”‚  keys of T:  'a' | 'b' | 'c' | 'd'           โ”‚
โ”‚  K:          'b'                             โ”‚
โ”‚                                              โ”‚
โ”‚  Exclude:    'a' | 'c' | 'd'                 โ”‚
โ”‚                                              โ”‚
โ”‚  Pick:       { a, c, d }                     โ”‚
โ”‚                                              โ”‚
โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜

Visual: When Each Makes Sense

โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”
โ”‚  Small change to a large type?               โ”‚
โ”‚       โ””โ”€โ”€ Omit (name the exceptions)         โ”‚
โ”‚                                              โ”‚
โ”‚  Large change to get a small view?           โ”‚
โ”‚       โ””โ”€โ”€ Pick (name the inclusions)         โ”‚
โ”‚                                              โ”‚
โ”‚  Optional fields for update?                 โ”‚
โ”‚       โ””โ”€โ”€ Partial                            โ”‚
โ”‚                                              โ”‚
โ”‚  Assert all fields present?                  โ”‚
โ”‚       โ””โ”€โ”€ Required                           โ”‚
โ”‚                                              โ”‚
โ”‚  Prevent mutation?                           โ”‚
โ”‚       โ””โ”€โ”€ Readonly                           โ”‚
โ”‚                                              โ”‚
โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜

Summary

UtilityEffect
Partial<T>Every property optional
Required<T>Every property required
Readonly<T>Every property readonly
Pick<T, K>Only K properties
Omit<T, K>All but K properties

Key takeaways:

  • Partial<T> makes every property optional โ€” the type for updates, patches, drafts
  • Required<T> makes every property required โ€” after applying defaults
  • Readonly<T> makes every property readonly โ€” for immutability
  • Pick<T, K> selects specific properties โ€” for views and projections
  • Omit<T, K> removes specific properties โ€” for views without sensitive fields
  • All five are mapped types โ€” the same pattern: iterate keys, apply a rule
  • Omit<T, K> = Pick<T, Exclude<keyof T, K>> โ€” the utilities compose
  • Pick requires K extends keyof T โ€” invalid keys error
  • Omit doesn’t require the keys to exist โ€” unknown keys are no-ops
  • All five are shallow โ€” nested objects aren’t affected; use DeepPartial<T> or DeepReadonly<T> for recursion
  • Combine them โ€” Partial<Omit<User, 'id'>> for update inputs
  • These are the vocabulary of TypeScript’s type transformations โ€” used in every library, API client, and state management system

Remember: Partial, Required, Readonly, Pick, and Omit are the five most-used utility types. Each reshapes an object type by applying a rule to its properties. They’re all small mapped types, and once you can read one you can read all. Combine them for real-world shapes โ€” Partial<Omit<T, 'id'>> for updates, Pick<T, 'id' \| 'name'> for previews, Omit<T, 'password'> for public views. They’re not magic; they’re the pattern. Learn them and you’ll see them everywhere.


Stop using slow, ad-bloated tool sites! ๐Ÿคฎ

๐Ÿ”Ž Search “KandZ Tools” on Google to use many professional utilities for free.

KandZ.me is the ultimate minimalist hub for:
โœ… Finance (Mortgage, Interest, Inflation)
โœ… Tech (Base64, JSON, Dev Suite, IP)
โœ… Health (BMI, BMR, TDEE)
โœ… Productivity (Timer, Workspace, QR)

โšก๏ธ Fast & Private
๐Ÿ”’ No data leaves your device
๐Ÿ’Ž 100% Free

๐Ÿ”— Use it now: https://tools.kandz.me
๐Ÿ”– Bookmark itโ€”youโ€™ll need it later!