Patterns
How do I stop `Omit<User, "nmae">` silently succeeding and removing nothing?
The built-in constrains its key parameter to `keyof any`, which is every key there could ever be. Constrain it to `keyof T` instead.
The recipe
type StrictOmit<T, K extends keyof T> = Omit<T, K> type User = { id: number; name: string; email: string } type WithoutEmail = StrictOmit<User, 'email'>
The build compiles this and checks each result below.
How it works
- 01
type StrictOmit<T, K extends keyof T> = Omit<T, K>
The entire recipe. The body still delegates to the built-in — only the door is narrower, and the door is where the bug was.
What you get
WithoutEmail
→{ id: number; name: string; }StrictOmit<User, 'id' | 'name'>
→{ email: string; }Omit<User, 'nmae'>→{ email: string; id: number; name: string; }The built-in, for comparison: no error, nothing removed, and a type that still looks plausible. This is the bug the constraint prevents.
Where it goes wrong
It is stricter than the built-in in a way that occasionally bites: `Omit` is deliberately permissive so it can be used on unions and on types whose keys are not yet known. Swapping it globally will surface real code that relied on that.
Takeaway
One line, and it removes an entire class of silent bug. Put it in the same file as your other shared types and never write bare `Omit` again.