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:
Requiredmakes everything required,Pickpicks properties,Omitomits 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 thanPartial<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 toRequired<T>internally, but exposePartial<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.
| Aspect | Readonly<T> | as const |
|---|---|---|
| Applied to | Type | Value |
| Result | Readonly type | Readonly literal type |
| Widening | Preserved | Prevented |
| Runtime | No effect | No 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 forreadonly T[]or a customDeepReadonly<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:
| Need | Use |
|---|---|
| Few properties | Pick<T, 'a' | 'b'> |
| Most properties | Omit<T, 'c' | 'd'> |
| Everything except sensitive | Omit<T, 'password'> |
| Specific view | Pick<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
PickandOmitexist: 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,Omitis clearer. With a small subset,Pickis 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โOmitremoves the sensitive fieldUserSummaryโPickselects the view fieldsCreateUserInputโOmitremoves server-generated fieldsUpdateUserInputโPartial<Omit<...>>combines two utilitiesFrozenUserโReadonlyprevents mutationFullUserโRequiredasserts 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
| Utility | Effect |
|---|---|
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
| Utility | Definition |
|---|---|
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
| Utility | Constraint |
|---|---|
Pick<T, K> | K extends keyof T |
Omit<T, K> | K extends keyof any (loose) |
Shallow vs Deep
| Utility | Depth |
|---|---|
Partial<T> | Shallow |
Required<T> | Shallow |
Readonly<T> | Shallow |
Pick<T, K> | One level |
Omit<T, K> | One level |
When to Use Each
| Need | Utility |
|---|---|
| Update payload | Partial<T> |
| Assert complete | Required<T> |
| Freeze for reading | Readonly<T> |
| Few fields | Pick<T, 'a' | 'b'> |
| Most fields | Omit<T, 'x' | 'y'> |
| Remove sensitive | Omit<T, 'password'> |
| Apply defaults | Required<Partial<T>> |
Pick vs Omit
| Aspect | Pick | Omit |
|---|---|---|
| Lists | What to include | What to exclude |
| K constraint | keyof T | Any string |
| Missing key | Error | No-op |
| Best when | Few fields | Many fields |
Combined Patterns
| Pattern | Result |
|---|---|
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
| Recipe | Type |
|---|---|
| Create input | Omit<T, 'id' | 'createdAt'> |
| Update input | Partial<Omit<T, 'id' | 'createdAt'>> |
| Public view | Omit<T, 'password'> |
| Summary view | Pick<T, 'id' | 'name'> |
| Immutable entity | Readonly<T> |
| Complete after defaults | Required<Config> |
Optional and Readonly Modifiers
| Modifier | Applied |
|---|---|
? | Partial |
-? | Required |
readonly | Readonly |
| (none) | Preserved |
Distributing Over Unions
| Type | Behavior |
|---|---|
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
| Error | Cause |
|---|---|
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 exist | Missing required property |
Cannot assign to readonly | Mutation attempt |
Interaction with Signals
| Type | Notes |
|---|---|
Partial<T> | Works with signal values |
Readonly<T> | Prevents signal.set on readonly signal |
Pick<T, K> | Selects signal keys |
Performance Notes
| Aspect | Detail |
|---|---|
| Compile-time | Shallow is fast |
| Deep recursion | Slows compilation |
Omit | Uses 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
| Pitfall | Problem | Solution |
|---|---|---|
Expecting deep Partial | Nested objects unchanged | Use recursive DeepPartial |
Readonly is shallow | Array/object contents mutable | Use readonly T[] |
Pick on union | Only shared keys | Distribute first |
Omit typo | Silent no-op | Check names |
Partial on primitive | Error | Only on objects |
Required after Partial | Restores all, not just some | Use conditional |
Pick with string K | Error | Use literal keys |
| Over-nesting utilities | Unreadable | Extract named types |
Readonly on function | Not applied to params | Annotate each |
Confusing Partial and Omit | Wrong shape | Partial 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
| Utility | Effect |
|---|---|
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, draftsRequired<T>makes every property required โ after applying defaultsReadonly<T>makes every property readonly โ for immutabilityPick<T, K>selects specific properties โ for views and projectionsOmit<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 composePickrequiresK extends keyof Tโ invalid keys errorOmitdoesn’t require the keys to exist โ unknown keys are no-ops- All five are shallow โ nested objects aren’t affected; use
DeepPartial<T>orDeepReadonly<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!