I suggest you change your code to look like this:
function reduceByProp<T, K extends PropertyKey>(
array: T[],
mapper: (a: T) => K
) {
return array.reduce(
(previous, current) => ({ ...previous, [mapper(current)]: current }),
{} as { [P in K]?: T }
);
}
Explaining the differences:
For your question, you can't do {[key: K]: T} or the like, since index signatures are constrained to be all strings or all numbers. Instead you can use mapped types of the form {[P in K]: T}.
Unless you want reduceByProp([{foo: 1}], v => "bar") to fail, you should make K extends PropertyKey and not K extends keyof T. keyof T is specifically only the keys inside the objects in your array, while PropertyKey is any key you want.
Don't annotate previous and current, or if you do annotate them, don't annotate them as T. current is definitely T, but previous is an accumulator and is not T but the return type of reduceByProp() which is something whose keys are returned by mapper() and whose value types are T.
Give the initial reduce object {} an explicit type, or otherwise specify what reduce() is expected to produce. The value {} will be inferred as type {} otherwise, which will fail to type check. So I've given it {} as ....
I've made the return type {[P in K]?: T} in which the properties are optional (?) instead of required as in {[P in K]: T}. The reason is that you might want to make a call like this:
reduceByProp([{ foo: 1 }, { foo: 3 }, { foo: 5 }], v => v.foo % 2 === 0 ? "even" : "odd");
The return type of that in my version is {even?: {foo: number}, odd?: {foo: number}}. It's good that those are optional because it turns out that the output has no even key at all.
Okay, hope that helps; good luck!
Playground link to code