-
Notifications
You must be signed in to change notification settings - Fork 25
new(rebuild, mapKeys) add types for new functions #140
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: develop
Are you sure you want to change the base?
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