Reputation: 99
I want to define a function that works like so:
const result = evaluate({x: () => 2 * 3, y: () => "hello"})
Where the value of result
is:
{x: 6, y: "hello"}
To the best of my knowledge, the definition has to look something like:
function evaluate<T>(obj: Record<keyof T, () => T[keyof T]>): T {
// Copy the key-value pairs, invoking each value.
// There are a few different ways to do this.
return Object.assign({}, ...Object.entries<() => T[keyof T]>(obj).map(([k, v]) => ({[k]: v()})))
}
However, this doesn't quite work. On the example provided earlier, the type of result
is inferred as:
{x: unknown, y: unknown}
The function call does work as expected if the type parameter is provided explicitly:
const result = evaluate<{x: number, y: string}>({x: () => 2 * 3, y: () => "hello"})
Is there some way I can get type inference to work correctly?
Upvotes: 0
Views: 280
Reputation: 249466
I don't think TS is smart enough to resolve T
in this way.
The solution is simple, you can capture in T
the type of the parameter and use a custom mapped type to get transform the parameter to the type you want:
const result = evaluate({x: () => 2 * 3, y: () => "hello"})
function evaluate<T extends Record<keyof T, () => any>>(obj: T): {
[P in keyof T]: ReturnType<T[P]>
} {
// Copy the key-value pairs, invoking each value.
// There are a few different ways to do this.
return Object.assign({}, ...Object.entries<() => T[keyof T]>(obj).map(([k, v]) => ({[k]: v()})))
}
Upvotes: 2