0

I have a function which takes an object and converts its keys to snake cased strings and returns it. I'm using reduce to construct the new object but can't seem to resolve this error:

Element implicitly has an 'any' type because type '{}' has no index signature.ts(7017)

Which occurs on the line return { ...o, [snakeCase(k)]: evt[k] }; stating that evt[k] has the any type. How do I resolve this? Are index signatures discarded because this is an empty object even though it has a type which has an index signature?

interface StringKeyMap<T> {
  readonly [key: string]: T[keyof T];
}

function snakeCaseKeys<T extends object>(evt: T): StringKeyMap<T> {
  const initial: StringKeyMap<T> = {};

  return Object.keys(evt).reduce<StringKeyMap<T>>((o, k) => {
    return { ...o, [snakeCase(k)]: evt[k] };
  }, initial);
}
Paul Dlug
  • 103
  • 2
  • 6

1 Answers1

2

The problem is that the return type of Object.keys(evt) is string[] and not Array<keyof T>. This is intentional, although a lot of people have reported this as an issue. Anyway, since k is just a string, then evt[k] is not considered safe, despite k coming from Object.keys().

The easiest way to address this is via a type assertion somewhere so that k is treated as keyof T. For example:

  return Object.keys(evt).reduce<StringKeyMap<T>>((o, k) => {
    const z =  { ...o, [snakeCase(k)]: evt[k as keyof T] };
    return z;
  }, initial); // no error

Hope that helps. Good luck!

jcalz
  • 264,269
  • 27
  • 359
  • 360
  • And this is the reason I still think the pragmatic thing to do is to type `keys` as `Array`... 9 out of 10 times (don't have actual metrics just gut feeling) you end up type asserting to `keyof T` anyway. Oh well :( – Titian Cernicova-Dragomir Mar 12 '19 at 13:48