Type definition for 'evolve' function

Viewed 299

I'm trying to write a simple type definition for evolve function from ramda. (https://ramdajs.com/docs/#evolve). Official definition does not work correctly.

type Transformation<State> = {
  [Key in keyof State]: (x: State[Key]) => any
}

declare function evolve
  <State extends {}, Evolver extends Partial<Transformation<State>>>(evolver: Evolver, state: State):
  {[Key in keyof State]: Evolver[Key] extends (...args: any[]) => {} ? ReturnType<Evolver[Key]> : State[Key]}

I'm trying to use that in a generic function:

const foo = <State extends {a: string, b: string}>(state: State) => {
  const test = evolve({
    a: x => x,
    b: x => x
  }, state)
}

but I'm getting an error:

Argument of type '{ a: (x: State["a"]) => State["a"]; b: (x: State["b"]) => State["b"]; }' is not assignable to parameter of type 'Partial<Transformation<State>>'.(2345)

It's not clear from the error why it's not assignable, so I don't know how to fix that

1 Answers

It looks like there was a couple things going on. I was able to get this to work by lifting Evolver in it's own type alias which also avoids an index issue when trying to identify it as the 2nd generic param. I also switched some return types to unknown to have ts infer them in the evolver object.

type Transformation<T> = {
  [K in keyof T]: (x: T[K]) => unknown;
};

type Evolver<State> = Partial<Transformation<State>>;

declare function evolve<State extends { [key: string]: unknown }>(
  evolver: Evolver<State>,
  state: State
): {
  [Key in keyof State]: Evolver<State>[Key] extends (...args: any[]) => infer R
    ? R
    : State[Key];
};

const foo = (state: { a: string; b: string }) => {
  const test = evolve(
    {
      a: (x) => x,
      b: (x) => x,
    },
    state
  );
};
Related