Search Terms:
Object.values Object.entries sound soundness unsound inconsistent Object.keys
Code
Proposed change: https://github.com/MicahZoltu/TypeScript/commit/603c36370c7fdcd142b1493ca8cbbb955db73b69
Related Issues:
Back in November 2016, a PR (#12207) was submitted to make the types of Object.entries and Object.values generic. This PR was reviewed and accepted, and as a note it was recommended that Object.keys be updated similarly. The author then submitted a PR to update Object.keys (#12253) but @ahejlsberg made the very valid point that this change was unsound and the PR was closed. The author of both PRs then suggested that perhaps #12207 should be reverted, but this revert never happened.
This issue is to discuss options for rectifying this situation and I propose we make Object.entries and Object.values consistent with the sound behavior of Object.keys. The major issue here is that this IS a breaking change since people may be relying on the currently unsound behavior of Object.values and Object.entries. However, I think having TypeScript be inconsistent on this front indefinitely is not good, and I don't think changing Object.keys to be unsound is the right solution, so at the least this change should be merged into TS 4.x.
It's an unfortunate situation but I can't imagine the upside to moving things in a direction that so many people would simply consider worse, even if it's more correct. I don't know if you've seen it, but we basically get a bug report or PR to change Object.keys to keyof T at least once a week.
Yeah, I have seen many of the Object.keys reports and it is a common question in TS Discord, people asking why Object.keys isn't generic. What bugs me the most at the moment is the inconsistency between Object.keys and Object.values/Object.entries. I would be willing to defer to the TS dev team's judgement on whether to go with generics or not and I can submit a PR to "fix" either.
Personally, I think that the sound solution is preferable, but I understand that often the TS dev team makes soundness sacrifices for usability reasons. The main reason I didn't lead with a PR to "fix" Object.keys is because my searching suggests that many have tried this in the past and it is consistently rejected, which is why I thought perhaps going the other direction would get more traction.
Hi,
I agree with you that Object.values and Object.entries become less confusing if they are soundly typed and consistent with Object.keys.
However, I think there are a few things to consider before changing them.
First, when an object is used as a record (or "dictionary"), the current typing of Object.values and Object.entries are useful.
const r: Record<string, number> = {
foo: 0,
bar: 1,
};
const vs = Object.values(r); // : number[]
const es = Object.entries(r); // : [string, number][]
If they are typed as vs: unknown[] and es: [string, unknown][], it becomes much inconvenient.
On the other hand, there is nothing wrong with Object.keys being soundly typed in this situation.
Second, the object spread operator has the same kind of unsoundness.
const a = { foo: 0, bar: 1 }; // : { foo: number, bar: number }
const b = { foo: 2, bar: "bar" };
const c: { foo: number } = b;
const d = { ...a, ...c }; // : { foo: number, bar: number }
As in the above code, the object spread operator ...obj works the same as the current Object.entries.
So I think changing the typing of Object.entries will introduce another kind of inconsistency.
What do you think?
Aha, I found it finally!
I'm having hard enough time justifying it to myself and explaining to my team mates why Object.keys returns string[]. And once I manage that, I have zero explanation why the same doesn't apply to Object.values. Even though the same code example that demonstrates soundness of Object.keys demonstrates the unsoundness of Object.values (or vice versa - depending on your view point).
I personally do not agree the behavior of Object.keys is sound - after all there are quite a lot of other ways to break static type guarantees (@susisu just demonstrated one of them in the comment above).
But no matter what, I believe consistency is even more important.
I think if Typescript is about improving developer experience, Object.keys should return (keyof T)[], it's easy enough to typecast it to make it be keyof T but, there should at least be something like Object.typeCastedKeys or something of that ilk.
If we are concerned about actual runtime type checking, TS team should develop a tool to actually check runtime types (during development only)
Most helpful comment
It's an unfortunate situation but I can't imagine the upside to moving things in a direction that so many people would simply consider worse, even if it's more correct. I don't know if you've seen it, but we basically get a bug report or PR to change
Object.keystokeyof Tat least once a week.