Description
lib Update Request
@jcalz has enumerated a non-exhaustive list here, the issue template links to the SO Answer. I'm aware of these resources, I know that this comes up quite frequently, but it would be greatly appreciated if you would hear me out 🙏
It's instructive to go way back to 2016 with this comment in a follow-up PR to #12207 -- which will be relevant in a few seconds.
Once you move to the instantiated type world it degenerates because an object can (and often does) have more properties at run-time than are statically known at compile time.
@RyanCavanaugh sort of codifies these hesitations in the SO answer. The crux of this argument is that a more-precisely typed Object.keys
is potentially unsound. We get a first hand demonstration of this by observing some well-typed program that would produce runtime errors.
But TS is already in this state where very similar code, is A) well-typed, but B) will produce runtime errors. This comes down to how Object.values
and Object.entries
are typed.
See two examples here, using Object.values
:
Note: this was known way back in 2016, where @ahejlsberg called this out in the same comment:
BTW, I have the same reservations about
Object.entries
which I notice is now typed this way.
Within a structural type-system and in the absence of Exact Types, it would appear that most Object reflection methods must be typed imprecisely wide, including Object.entries
and Object.values
.
But this kind of feels like it violates Typescripts third non-goal: Precise types for object methods are incredibly useful, which is clear by the number of requests, questions and PRs around Object.keys
type signature.
Given the state of things it feels like one of two things should happen:
- Remove these two type signatures, and revisit when Exact Types land.
- Add a more precise
Object.keys
type definition override, and maintain at least the same level of unsoundness that already exists.
Sample Code
Something like this maintains parity with the status-quo:
interface ObjectConstructor {
keys<T extends Record<string, unknown>>(o: T): (keyof T)[];
}
Which is not a valid program in the SO example, but demonstrates the same level of unsoundness that already exists
Thanks for listening, appreciate y'all 😄 . I realize the team is full of experts and this is likely to have been discussed. Happy to close if thats the case, but am interested to read more about it!