new(rebuild, mapKeys) add types for new functions#140
new(rebuild, mapKeys) add types for new functions#140Harris-Miller wants to merge 5 commits intodevelopfrom
Conversation
|
https://stackoverflow.com/questions/55454125/typescript-remapping-object-properties-in-typesafe For the needs of my project I came up with this compilation. type TuplesFromObject<T> = {
[P in keyof T]: [P, T[P]];
}[keyof T];
type GetKeyByValue<T, V> =
TuplesFromObject<T> extends infer TT
? TT extends [infer P, V]
? P
: never
: never;
export type Remap<T, U extends { [P in keyof T]?: V }, V extends string> = {
[P in NonNullable<U[keyof U]>]: T[GetKeyByValue<U, P>];
};
export type Intact<T, U> = { [K in Exclude<keyof T, keyof U>]: T[K] }; |
|
Ugh ok this one sucks @RobinTail thanks for the suggestion, that worked great for That isn't really possible for For now, I'm choosing to have those both return |
|
Actually, I may update this MR to just add |
14a24a4 to
5a0aded
Compare
|
I cherry-picked over @RobinTail feel free to contribute directly to this PR, I honestly am not going to have the time until at least September to work on this more |
|
Ok, I will take a look, @Harris-Miller |
@Harris-Miller , I don't have permissions to contribute But here is my proposition on improving the overloads having all arguments! Date: Sat, 26 Jul 2025 00:40:04 +0200
Subject: [PATCH] Improving overloads having all arguments, test for mapKeys.
---
test/mapKeys.test.ts | 19 +++++++++++++++++++
test/rebuild.test.ts | 8 ++++----
types/mapKeys.d.ts | 2 +-
types/rebuild.d.ts | 2 +-
4 files changed, 25 insertions(+), 6 deletions(-)
create mode 100644 test/mapKeys.test.ts
diff --git a/test/mapKeys.test.ts b/test/mapKeys.test.ts
new file mode 100644
index 0000000..dbac7c2
--- /dev/null
+++ b/test/mapKeys.test.ts
@@ -0,0 +1,19 @@
+import { expectType } from 'tsd';
+
+import { mapKeys } from '../es';
+
+const subject1 = { foo: '123-456', bar: '678' };
+
+// poorly typed function
+const result1 = mapKeys((key) => key.toUpperCase(), subject1);
+expectType<Record<string, string>>(result1);
+
+// well typed function
+const result2 = mapKeys((key) => key.toUpperCase() as Uppercase<typeof key>, subject1);
+expectType<Record<'FOO' | 'BAR', string>>(result2);
+
+// different value types
+const subject2 = { foo: 123, bar: 'blah' };
+
+const result3 = mapKeys((key) => key.toUpperCase() as Uppercase<typeof key>, subject2);
+expectType<Record<'FOO' | 'BAR', string | number>>(result3);
diff --git a/test/rebuild.test.ts b/test/rebuild.test.ts
index ce0be71..86f864b 100644
--- a/test/rebuild.test.ts
+++ b/test/rebuild.test.ts
@@ -8,22 +8,22 @@ const newObj = rebuild(([k, v]) => {
return v.split('-').map((n, i) => [`${k}${i}`, n]);
}, oldObj);
-expectType<Record<string, string>>(newObj);
+expectType<Record<`foo${number}` | `bar${number}`, string>>(newObj);
const newObj2 = rebuild(([k, v]) => {
return [[k, v.split('-')]];
}, oldObj);
-expectType<Record<string, string[]>>(newObj2);
+expectType<Record<'foo' | 'bar', string[]>>(newObj2);
const newObj3 = rebuild(([k, v]) => {
const innerObj = Object.fromEntries(v.split('-').map((n, i) => [i, n]));
return [[k, innerObj]];
}, oldObj);
-expectType<Record<string, Record<string, string>>>(newObj3);
+expectType<Record<'foo' | 'bar', Record<string, string>>>(newObj3);
const diffValueTypes = { foo: 123, bar: 'blah' };
const updated = rebuild(([k, v]) =>[[k, v]], diffValueTypes);
-expectType<Record<string, string | number>>(updated);
+expectType<Record<'foo' | 'bar', string | number>>(updated);
diff --git a/types/mapKeys.d.ts b/types/mapKeys.d.ts
index f89dcb7..fe70498 100644
--- a/types/mapKeys.d.ts
+++ b/types/mapKeys.d.ts
@@ -5,4 +5,4 @@ export function mapKeys(fn: (key: string) => string): <T>(obj: Record<string, T>
// mapKeys(__, obj)(fn)
export function mapKeys<T>(__: Placeholder, obj: Record<string, T>): (fn: (key: string) => string) => Record<string, T>;
// mapKeys(fn, obj)
-export function mapKeys<T>(fn: (key: string) => string, obj: Record<string, T>): Record<string, T>;
+export function mapKeys<T extends Record<string, unknown>, U extends string>(fn: (key: keyof T) => U, obj: T): Record<U, T[keyof T]>;
diff --git a/types/rebuild.d.ts b/types/rebuild.d.ts
index d8c92e3..44cca23 100644
--- a/types/rebuild.d.ts
+++ b/types/rebuild.d.ts
@@ -5,4 +5,4 @@ export function rebuild<T, V>(fn: (kvp: [keyof T, T[keyof T]]) => [string, V][])
// rebuild(__, obj)(fn)
export function rebuild<T>(__: Placeholder, obj: T): <V>(fn: (kvp: [keyof T, T[keyof T]]) => [string, V][]) => Record<string, V>;
// rebuild(fn, obj)
-export function rebuild<T, V>(fn: (kvp: [keyof T, T[keyof T]]) => [string, V][], obj: T): Record<string, V>;
+export function rebuild<T, V, U extends string>(fn: (kvp: [keyof T, T[keyof T]]) => [U, V][], obj: T): Record<U, V>; |
|
Similarly, I'd also like to propose this: - export function mapKeys<T>(__: Placeholder, obj: Record<string, T>): (fn: (key: string) => string) => Record<string, T>;
+ export function mapKeys<T extends Record<string, unknown>>(__: Placeholder, obj: T): <U extends string>(fn: (key: keyof T) => U) => Record<U, T>;and - export function rebuild<T>(__: Placeholder, obj: T): <V>(fn: (kvp: [keyof T, T[keyof T]]) => [string, V][]) => Record<string, V>;
+ export function rebuild<T>(__: Placeholder, obj: T): <V, U extends string>(fn: (kvp: [keyof T, T[keyof T]]) => [U, V][]) => Record<U, V>;It would add constraints to the postponed function, limiting the keys to the ones of the original subject, also improving the type of the keys in the resulting object. |
Types for new function added in ramda/ramda#3493