TypeScript:为评估通用对象的函数属性的函数推断正确的类型
TypeScript: Inferring the correct type for a function that evaluates function properties on a generic object
我想定义一个像这样工作的函数:
const result = evaluate({x: () => 2 * 3, y: () => "hello"})
其中result
的值为:
{x: 6, y: "hello"}
据我所知,定义必须类似于:
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()})))
}
然而,这并不完全有效。在前面提供的示例中,result
的类型被推断为:
{x: unknown, y: unknown}
如果明确提供类型参数,函数调用 会按预期工作:
const result = evaluate<{x: number, y: string}>({x: () => 2 * 3, y: () => "hello"})
有什么方法可以让类型推断正常工作吗?
我认为 TS 不够聪明,无法以这种方式解决 T
。
解决方案很简单,您可以在 T
中捕获参数的类型并使用自定义映射类型将参数转换为您想要的类型:
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()})))
}
我想定义一个像这样工作的函数:
const result = evaluate({x: () => 2 * 3, y: () => "hello"})
其中result
的值为:
{x: 6, y: "hello"}
据我所知,定义必须类似于:
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()})))
}
然而,这并不完全有效。在前面提供的示例中,result
的类型被推断为:
{x: unknown, y: unknown}
如果明确提供类型参数,函数调用 会按预期工作:
const result = evaluate<{x: number, y: string}>({x: () => 2 * 3, y: () => "hello"})
有什么方法可以让类型推断正常工作吗?
我认为 TS 不够聪明,无法以这种方式解决 T
。
解决方案很简单,您可以在 T
中捕获参数的类型并使用自定义映射类型将参数转换为您想要的类型:
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()})))
}