ecl3ctic
ecl3ctic

Reputation: 99

TypeScript: Inferring the correct type for a function that evaluates function properties on a generic object

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

Answers (1)

Titian Cernicova-Dragomir
Titian Cernicova-Dragomir

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()})))
}

Playground Link

Upvotes: 2

Related Questions