Omit and Pick are some of the most loved utility types in TypeScript. They let you create new types by excluding or selecting specific properties from an existing type.
However, when you use them with union types, their behavior can be misleading - and in some cases, break your type expectations
type Employee = {
id: string
name: string
job_name: string
}
type Customer = {
id: string
name: string
company: string
}
type Person = Employee | Customer
❌ Problem: Using Omit Directly
Suppose you want to remove the id field from all personas:
type PersonWithoutId = Omit<Person, 'id'>
🧨 Actual Result:
type PersonWithoutId = {
name: string
}
Only name survives. The fields job_name and company are lost.
Why?
Because Omit is not distributive over union types. It collapses the union into a common structure, then removes the property - resulting in a simplified, lossy type.
✅ Solution:
DistributiveOmit
We can fix this by explicitly distributing Omit across each union member:
type DistributiveOmit<T, K extends PropertyKey> = T extends any
? Omit<T, K>
: never
type PersonWithoutId = DistributiveOmit<Person, 'id'>
✅ Expected Result:
type PersonWithoutId =
| { name: string; job_name: string }
| { name: string; company: string }
🧪 Same Fix for Pick
type DistributivePick<T, K extends keyof T> = T extends any
? Pick<T, K>
: never
type BasicaPerson = DistributivePick<Person, 'id' | 'name'>
🧠 Summary Table
Top comments (0)