TypeScript中mapValue函数回调参数的类型窄化问题
为对象值重映射函数编写精确的TypeScript类型声明
我正在为一个用于重映射对象值的函数编写TypeScript类型声明,函数的使用示例如下:
const result = mapValue( { foo: "a", bar: 10, baz: true, }, [ ["foo", (value) => `thing ${value}`], ["bar", (value) => value - 1], ] ); result; // { foo: "thing a", bar: 9 }
该函数的第二个参数是一个二维数组,每个元素包含要重映射的键和用于计算新值的回调函数。目前我遇到的核心问题是:无法将传递给映射回调的参数类型正确窄化。例如,对于映射项["foo", (value) => value],value的类型被推断为string | number | boolean,但它应该被窄化为仅string类型。
以下是类型安全性不足的初步版本:
declare function mapValue<T, TKey extends keyof T, TResult>( obj: T, mapping: Array<[TKey, (x: T[TKey]) => TResult]>, ): Record<typeof mapping[number][0], ReturnType<typeof mapping[number][1]>>;
我尝试使用映射类型逐步推断mapping参数,但TypeScript无法从TMapped[i][0]中正确推断类型:
declare function mapValue<T, const TMapping>( obj: T, mapping: { [i in keyof TMapping]: TMapping[i] extends [infer key, any] ? [TMapping[i][0], (value: T[key]) => unknown] : never }, ): "todo";
解决方案
要实现精确的类型推断,需要让TypeScript为每个映射项单独关联对应的键类型和值类型,同时正确推导返回对象的结构。可以通过以下类型声明实现:
type MapEntry<T, K extends keyof T> = [K, (value: T[K]) => unknown]; declare function mapValue< T extends object, M extends MapEntry<T, keyof T>[] >( obj: T, mapping: readonly [...M] ): { [K in M[number][0]]: (Extract<M[number], [K, any]>[1]) extends (v: any) => infer R ? R : never };
使用这个类型声明后,示例中的value参数会被正确窄化:
- 对应
foo的回调参数类型为string - 对应
bar的回调参数类型为number
返回值result的类型会被精确推断为{ foo: string; bar: number },完全符合预期。
内容的提问来源于stack exchange,提问作者Anatole Lucet
相关产品推荐
相关产品推荐

