better-typescript-lib
better-typescript-lib copied to clipboard
refactor: Change return type of Object.entries from [string, V] to [K, V]
Currently, the return value of Object.entries
is [string, V], which is weakly typed when extracted by subsequent map
, etc.
Therefore, we propose to return [K,V] so that the type of the key can be inferred.
However, we do not consider other detailed use cases. If it is not appropriate, please CLOSE.
Supplemental
I ran the test and it mocked up loudly🤯.
This is not true. Object.entries
ignores symbol keys, and returns strings even for numeric keys, as they are actually strings.
Maybe K extends symbol ? never : `${K}`
would be a good solution, but this may cause issues related to variance.
FWIW, I'm using the following locally:
type KeyToString<K extends number | string | symbol> = keyof {
[key in K as key extends string ? key : key extends number ? `${key}` : never]: unknown;
};
declare global {
interface ObjectConstructor {
keys<K extends number | string | symbol, V>(o: Record<K, V>): KeyToString<K>[];
entries<K extends number | string | symbol, V>(o: Record<K, V>): [KeyToString<K>, V][];
values<K extends number | string | symbol, V>(o: Record<K, V>): V[];
}
}
It works well for my code, but there might be edge cases that I didn't consider.
The above seems to not work with the TypeScript 5.4 beta because the resulting keys still include number
. I'm not sure whether that's a bug in the beta or not, but as a workaround we can intersect with string
:
type KeyToString<K extends number | string | symbol> = string &
keyof {
[key in K as key extends string ? key : key extends number ? `${key}` : never]: unknown;
};
Though actually, I can't remember why I made it that complicated in the first place. This also seems to work just fine:
type KeyToString<K extends number | string | symbol> = K extends string ? K : K extends number ? `${K}` : never;
I came across some issues with the current implementation and the above suggestions in https://github.com/uhyo/better-typescript-lib/issues/40. Here's a version that works for those cases too:
type KeyToString<K extends PropertyKey> = K extends string ? K : K extends number ? `${K}` : never;
interface ObjectConstructor {
keys<K extends PropertyKey, V>(o: [K, V] extends [never, never] ? never : Record<K, V>): KeyToString<K>[];
values<K extends PropertyKey, V>(o: [K, V] extends [never, never] ? never : Record<K, V>): V[];
entries<K extends PropertyKey, V>(o: [K, V] extends [never, never] ? never : Record<K, V>): [KeyToString<K>, V][];
}