Typescript: Object.values and Object.entries are unsound and inconsistent with Object.keys.

Created on 13 May 2020  ·  6Comments  ·  Source: microsoft/TypeScript

Search Terms:
Object.values Object.entries sound soundness unsound inconsistent Object.keys

Code
Proposed change: https://github.com/MicahZoltu/TypeScript/commit/603c36370c7fdcd142b1493ca8cbbb955db73b69

Related Issues:

12207

12253

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.

Awaiting More Feedback Suggestion

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.keys to keyof T at least once a week.

All 6 comments

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)

Was this page helpful?
0 / 5 - 0 ratings

Related issues

fwanicka picture fwanicka  ·  3Comments

Antony-Jones picture Antony-Jones  ·  3Comments

CyrusNajmabadi picture CyrusNajmabadi  ·  3Comments

bgrieder picture bgrieder  ·  3Comments

wmaurer picture wmaurer  ·  3Comments