YOu have sevaral ways to handle it:
First:
If you use reference instead of literal type, it works as expected:
const id = <T,>(x: T): T => x
const keys: string[] = []
const iter = keys.map(k => [
k,
id({ test() { } }) // ok
])
const obj2 = Object.fromEntries(iter)
OR:
const idResult = id({ test() { } })
const obj2 = Object.fromEntries(keys.map(k => [
k,
idResult
]))
Second
Let's try to use arrow function as property instead of method:
const obj2 = Object.fromEntries(keys.map(k => [
k,
id({ test: () => { } }) // ok
]))
Strange behavior, is not it?
Let's take a look on Object.fromEntries signature:
fromEntries<T = any>(entries: Iterable<readonly [PropertyKey, T]>): { [k: string]: T };
So, TS tries to infer T generic type. In our case it is ReturnType of id function.
But for some reason, TS is unable to do it.
Third:
In order to help TS to infer the type, you can provide explicit generic argument for Array.prototype.map
const obj2 = Object.fromEntries(
keys.map<[string, { test(): void }]>((k) => [k, id({ test() {} })])
);
Fourth
You can also handle it without any explicit generic arguments.
You can just add constraint to T generic in your id function definition.
interface Method {
(): void;
}
const id = <T extends { [prop: string]: Method }>(x: T) => x;
const keys: string[] = [];
const obj2 = Object.fromEntries(keys.map((k) => [k, id({ test() {} })])); // ok
/////////////////////////
type ArrowProp = () => any;
const id = <T extends { [prop: string]: ArrowProp }>(x: T) => x;
const keys: string[] = [];
const obj2 = Object.fromEntries(keys.map((k) => [k, id({ test() {} })]));
Fifth
You can even use second generic to help TS to infer the type
const id = <Prop, T extends Record<string, Prop>>(x: T) => x;
// OR
const id = <T extends object>(x: T) => x;
const keys: string[] = [];
const obj2 = Object.fromEntries(keys.map((k) => [k, id({ test() {} })]));
Unfortunately, I'm unable to explain why TS can't infer method property without any workarounds