better-typescript-lib icon indicating copy to clipboard operation
better-typescript-lib copied to clipboard

refactor: Change return type of Object.entries from [string, V] to [K, V]

Open ushironoko opened this issue 1 year ago • 5 comments

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🤯.

ushironoko avatar Jun 14 '23 09:06 ushironoko

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.

graphemecluster avatar Jun 15 '23 15:06 graphemecluster

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.

ehoogeveen-medweb avatar Sep 18 '23 14:09 ehoogeveen-medweb

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;
	};

ehoogeveen-medweb avatar Jan 30 '24 12:01 ehoogeveen-medweb

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;

ehoogeveen-medweb avatar Jan 30 '24 12:01 ehoogeveen-medweb

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][];
}

ehoogeveen-medweb avatar May 13 '24 19:05 ehoogeveen-medweb