Pick<T, K> and Omit<T, K> utility types construct derived shapes directly from your master domain models.1. Pick<T, K> (Selecting Specific Properties)#
Pick<T, K> constructs a new type by selecting a specific set of keys (K) from a source object type (T).
The second parameter K must extend keyof T.
Production Use Case: Public User Preview DTO#
When returning user details in a public search endpoint, sensitive fields like passwordHash, twoFactorSecret, and stripeCustomerId must be excluded:
interface DatabaseUser {
id: string;
username: string;
email: string;
passwordHash: string;
twoFactorSecret: string;
stripeCustomerId: string;
createdAt: Date;
updatedAt: Date;
}
// 🟢 Pick ONLY 'id', 'username', and 'email' for public API responses:
type PublicUserDto = Pick<DatabaseUser, "id" | "username" | "email">;
/* Inferred Type:
{
id: string;
username: string;
email: string;
}
*/
const publicUser: PublicUserDto = {
id: "usr_9918",
username: "alice_dev",
email: "alice@acme.com",
};How Pick<T, K> is Defined Under the Hood:#
type CustomPick<T, K extends keyof T> = {
[P in K]: T[P];
};Notice how K extends keyof T enforces that you cannot Pick a property key that does not exist on T:
// ❌ Compiler Error: Type '"nonExistentKey"' is not assignable to type 'keyof DatabaseUser'.
// type BadPick = Pick<DatabaseUser, "id" | "nonExistentKey">;
2. Omit<T, K> (Excluding Specific Properties)#
Omit<T, K> does the exact opposite of Pick<T, K>. It constructs a type by taking all properties from T and stripping away the specified keys (K).
Production Use Case: Create Entity Payload DTO#
When a client submits a POST request to create a new database user, the client cannot supply id, createdAt, or updatedAt (because the database auto-generates them upon insertion):
// 🟢 Omit auto-generated database columns and sensitive security credentials:
type CreateUserPayloadDto = Omit<DatabaseUser, "id" | "createdAt" | "updatedAt" | "stripeCustomerId">;
/* Inferred Type:
{
username: string;
email: string;
passwordHash: string;
twoFactorSecret: string;
}
*/
function createUser(payload: CreateUserPayloadDto): PublicUserDto {
console.log(`Inserting ${payload.username} into DB...`);
return {
id: "usr_new_101",
username: payload.username,
email: payload.email,
};
}How Omit<T, K> is Defined Under the Hood:#
Omit combines Pick with the Exclude utility type:
type CustomOmit<T, K extends keyof any> = Pick<T, Exclude<keyof T, K>>;3. Key Constraint Difference: Pick vs Omit#
A subtle technical difference between Pick and Omit lies in their constraint parameters:
Pick<T, K extends keyof T>:KMUST be a valid key ofT. If you attempt to pick an invalid key name,tscthrows an error.Omit<T, K extends keyof any>:Kis constrained tokeyof any(string | number | symbol). Omitting a key name that does not exist onTwill NOT throw a compiler error—it simply returnsTunchanged.
interface Product {
sku: string;
price: number;
}
// ❌ Error: Type '"discount"' is not assignable to 'keyof Product'
// type BadPick = Pick<Product, "sku" | "discount">;
// 🟢 Allowed: 'discount' is ignored because it doesn't exist on Product
type SafeOmit = Omit<Product, "discount">; // Result: { sku: string; price: number }
4. Combining Utility Types (Partial<Omit<T, K>>)#
In real-world application architectures, utility types are frequently composed together to create complex DTO pipeline types:
// Create DTO for PATCH update requests:
// 1. Omit immutable system fields ('id', 'createdAt')
// 2. Wrap remaining editable fields in Partial<T> so all update keys are optional!
type PatchUserDto = Partial<Omit<DatabaseUser, "id" | "createdAt" | "updatedAt">>;
/* Inferred Type:
{
username?: string;
email?: string;
passwordHash?: string;
twoFactorSecret?: string;
stripeCustomerId?: string;
}
*/5. Decision Matrix: Pick vs Omit#
| Scenario | Recommended Choice | Rationale |
|---|---|---|
| Extracting 2 properties from an object with 20 properties | Pick | Explicitly lists the small subset of required keys. |
| Excluding 2 properties from an object with 20 properties | Omit | Cleaner than listing 18 keys inside a Pick. |
| Creating Public API / DTO previews | Pick | Secure: If new sensitive fields are added to T later, Pick won’t accidentally leak them. |
| Creating Form Input / Insert DTOs | Omit | Efficient: Removes auto-generated IDs while keeping all other fields required. |
Security Tip: Use Pick when creating public API DTOs. If an engineer adds a new ssnNumber property to DatabaseUser in the future, Omit might accidentally expose it if the engineer forgets to add ssnNumber to the exclusion array, whereas Pick will safely ignore the new field by default!
Summary & Next Steps#
In this episode:
- We analyzed
Pick<T, K>and its under-the-hood mapped type definition ([P in K]: T[P]). - We analyzed
Omit<T, K>and its composition viaPick<T, Exclude<keyof T, K>>. - We compared key constraint differences (
K extends keyof TvsK extends keyof any). - We composed
Partial<Omit<T, K>>for update DTOs. - We established the Security Rule: Prefer
Pickfor public DTOs to prevent accidental credential leakage during future schema expansions.
In Episode 35: Utility Types (Record<K, V>), we will explore how to build type-safe dictionary maps and lookup tables using Record!

