1

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.

3
  • 1
    For the typing you could do something like this if you can statically represent the renaming function. But really, with F, all you're going to be able to do is something like Record<ReturnType<F>, M[keyof M]>, which is pretty wide and maybe not worth your time Commented Jun 13, 2019 at 16:18
  • Worth it or not? Commented Jun 13, 2019 at 16:26
  • Agree with @jcalz. In short, it can't be done nicely because you are creating a new type at runtime with a generic transformation. Commented Jun 13, 2019 at 17:08

1 Answer 1

1

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}
Sign up to request clarification or add additional context in comments.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.