1
const arrayToObject = <T>(array: T[], key: keyof T): Record<string, T> => {
  return array.reduce(
    (accumulator: Record<string, T>, currentValue: T) =>
      Object.assign(accumulator, {
        [(currentValue[key] as string).toString()]: currentValue,
      }),
    {} as Record<string, T>
  );
};

is there anyway to avoid using "as string"? how can I have type check that the value of property "key" in type T is always a string?

type T {
    someOtherProperty: 20,
    active: false,
   [key]: string; (the value type of the property "key" must be string)
}

2 Answers 2

1

You could make arrayToObject() generic in both the array element type T and the relevant key type K, and constrain T so that its property at key K is assignable to string:

const arrayToObject = <T extends Record<K, string>, K extends keyof T>(
  array: T[], key: K
): Record<string, T> => {
  return array.reduce<Record<string, T>>(
    (accumulator, currentValue) =>
      Object.assign(accumulator, {
        [currentValue[key]]: currentValue,
      }),
    {}
  );
};

That compiles without error, and you can test that it works:

interface Foo {
  x: number,
  y: string,
  z: boolean
}

const foos: Foo[] = [
  { x: 1, y: "a", z: true }, 
  { x: 2, y: "b", z: false }, 
  { x: 3, y: "c", z: true }
];

const obj = arrayToObject(foos, "y"); // okay

Object.entries(obj).forEach(
  ([k, v]) => console.log(k, v.x.toFixed(1), v.y.toUpperCase())
);
/* "a",  "1.0",  "A" 
   "b",  "2.0",  "B" 
   "c",  "3.0",  "C" */ 

arrayToObject(foos, "x") // error!
// ---------> ~~~~
// Types of property 'x' are incompatible.

Looks good. The compiler will accept a correct call but complain if you pass it the wrong key (although it will say it's the wrong array, as it uses the key to check the array element type and not vice versa... but the error is clear, foos' elements don't have a string-valued x property).

Playground link to code

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

Comments

0

This will do :

const arrayToObject = <T extends Record<any, string>>(array: T[], key: keyof T): Record<string, T> => {
  return array.reduce(
    (accumulator: Record<string, T>, currentValue: T) =>
      Object.assign(accumulator, {
        [currentValue[key].toString()]: currentValue,
      }),
    {}
  );
};

Playground

1 Comment

Hi, I forgot to mention that there are other properties which are not string type, I only specifically want the property "key" to be string type

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.