0

I want to get the key map from any object,

I've implemented the function,

but I can't make typescript happy,

what should I do without using acc: any

const origin = {
  a: 1,
  b: 'string',
  c: () => {}
};

function getObjKeyMap<T>(obj: T) {
  return Object.keys(obj).reduce((acc, k) => {
 /* Element implicitly has an 'any' type because expression of type 'string' can't be used to index type '{}'.
  No index signature with a parameter of type 'string' was found on type '{}'.ts(7053) */
    acc[k] = k;
    return acc;
  }, {});
}

const keyMap = getObjKeyMap(origin);

1 Answer 1

2

You need to tell the type of object that reduce uses for accumulator.

const originObj = {
  a: 1,
  b: 'string',
  c: () => {}
};

function getObjKeyMap<T>(obj: T) {
  return Object.keys(obj).reduce((acc, k) => {
    acc[k] = k;
    return acc;
  }, {} as Record<string, string>);
}

// or


function getObjKeyMap1<T>(obj: T) {
  return Object.keys(obj).reduce((acc: Record<string, string>, k: string) => {
    acc[k] = k;
    return acc;
  }, {});
}

const keyMap = getObjKeyMap(originObj);

TS Playground

However, I bet this is not the best solution. I bet there is a way to write a function declaration like this:

function getObjKeyMap<T, K extends keyof T>(obj: T): Record<K, K>

Where Typescript automatically tells you that getObjectMap({a: 1, b: 'string', c: () => {}} returns {a: "a", b: "b", c: "c"}.

I think this is much better:

const originObj = {
  a: 1,
  b: 'string',
  c: () => {}
};

type KeyMap<T> = {
  [key in keyof T]: key
};

function getObjKeyMap<T>(obj: T): KeyMap<T> {
  return Object.keys(obj).reduce((acc, k) => ({...acc, k: k}), {} as KeyMap<T>);
}


const keyMap = getObjKeyMap<typeof originObj>(originObj);

TS Playground

Sign up to request clarification or add additional context in comments.

4 Comments

Rather than using {} as T can't you just specify what the accumulator is inside of reduce's type arguments?
Yes, if you see the first snippet, getObjKeyMap1 does that. In the second, I though the OP would understand that we can avoid as if we specify it in the parameter. I guess your form looks much neater. I'd rather edit the answer to do that. Thanks.
Sorry, I meant something like .reduce<T>(...). I believe this is the intended way? Either way, it will still work :) I was just asking a question that I wasn't sure of
Ah, where was my mind. :) Though, for some reason, it does not work that easily, see here: tsplay.dev/DWKA8N I tried casting Object.keys(obj) as [keyof T], no avail.

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.