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