Reputation: 143
I have a function that takes a string transformation function and a an object and applies the transformation to all of the keys, returning the resulting object.
In particular I'm using Ramda
Here is my function (untyped for clarity).
const renameBy = R.curry(
(fn, obj) => R.pipe(
R.toPairs,
R.map([k, v] => [fn(k), v]),
R.fromPairs
)(obj)
);
I'm well aware of the issues currying can cause, so I'm really only looking for solutions addressing the core function (and optionally the second unary function, renameBy(fn)) and resolving a nicer return type than the current R.Dictionary, which basically never matches anything.
I've been looking here, dealing with renaming specific keys of an object and here, dealing with recursive, deep nested-object renaming but neither seems to quite be what I'm looking for.
Example of usage
const input = {
foo_bar: "foobar",
bar: "bar",
bar_foo: "barfoo"
}
const camelize = (paramName) =>
paramName.replace(
/_(.?)/g,
$1 => $1.substr(1).toUpperCase()
);
renameBy(camelize, input)
const expectedOutput = {
fooBar: "foobar",
bar: "bar",
barFoo: "barfoo"
}
It's possible I could use something like
function renameBy<
F extends (str: string) => string,
N extends string[],
M extends Record<string, any>
>(fn: F, obj: M): { [P in N]: M[keyof M] }
{...}
But I can't quite get it to work.
Upvotes: 1
Views: 859
Reputation: 5061
It has been a while since this question was discussed. Recently, I faced a similar task. Anyway, here is a possible solution with TS 4.1+
// helper types
type BaseT = Record<string, unknown> // object constraint
type BaseM = Record<string, string> // mapping constraint
type KVs<T extends BaseM> = keyof T | T[keyof T] // keys and values
type Remap<T extends BaseT, M extends BaseM> =
& {[K in keyof M & keyof T as M[K]]: T[K]} // new keys
& {[K in keyof T as Exclude<K, KVs<M>>]: T[K]} // unchanged keys
type Result = Remap<{a: 1; b: '2'}, {a: 'c'}>
// type Result = {b: '2'} & {c: 1}
Upvotes: 1