Reputation: 12328
Background: (can be skipped)
I was fiddling around to make some update(person: Person)
statement more usable by allowing a subset of properties it should update. I was thinking there are 2 options:
Partial<Person>
or Omit<Person, 'id'>
as the 2nd argument.id
property is in the object provided.So Omit
seems like a good candidate to me, but the keys to omit can be of any string value.
Question:
Why does TypeScript not enforce the value of the keys provided to Omit
? Is there any good use not to which I am overseeing?
See the following code
export interface Person {
id: string;
age: number;
name: string;
}
type UpdatePerson = Omit<Person, 'whatEver'>; // Why does 'whatEver' not cause compile issues?
type UpdatePerson2 = Pick<Person, 'id'> & Partial<Person>; // Great, a valid key is enforced here
type UpdatePerson3 = Pick<Person, 'thisDoesNotCompile'> & Partial<Person>; // This line fails compilation as that property isn't part of the interface
For now I go with update(person: UpdatePerson)
and type UpdatePerson = Pick<Person, 'id'> & Partial<Person>;
. This makes all properties optional, and the id mandatory.
Upvotes: 3
Views: 1951
Reputation: 4381
The built-in Omit utility in TypeScript is implemented like this:
type Omit<T, K extends keyof any> = Pick<T, Exclude<keyof T, K>>;
Notice that K
is constrained by keyof any, which allows any string-based keys, even those not present on T
. This design allows for flexibility but sacrifices strictness regarding the keys in K
.
To make a safer version of Omit that ensures only keys present on T
can be omitted, you can redefine it with a stricter constraint on K
:
type StrictOmit<T, K extends keyof T> = Pick<T, Exclude<keyof T, K>>;
In this custom StrictOmit, K
is constrained specifically to keyof T
, meaning only properties actually existing on T
can be specified for omission. If you try to omit a key that doesn’t exist on T
, TypeScript will raise an error.
To wrap things up just place the custom type in the global.d.ts
and youre good to go.
Upvotes: 1
Reputation: 95614
In short, this behavior was selected so the built-in Omit
could be most compatible with existing DefinitelyTyped libraries. Those libraries were already split on whether Omit should be strict, and picking a looser Omit prevented those existing libraries from breaking at the expense of slightly less compiler safety and autocomplete for code yet to be written.
For background: this was requested in microsoft/TypeScript#30825, with this response from DanielRosenwasser:
It seems like the constrained
Omit
type is going to make at least half of users unhappy based on declarations within DefinitelyTyped. We've decided to go with the more permissive built-in which your own constraints can build on.
And these from language co-author Ryan Cavanaugh:
I have to clear up this misconception. There were 12 different definitions of Omit on DT and the two most popular definitions differed on whether or not to constraint [sic] the key: [snip]
You can pick at the numbers and try to declare a democratic majority or something, but the reality is that only one definition doesn't break a substantial portion of people.
It is a popular request with duplicates here on SO and on GitHub, and corresponding concise strict equivalents are easy to write/derive and also available in other libraries like type-zoo
.
Upvotes: 4