如何将Record的键作为索引,使函数参数类型匹配对应属性类型?
解决TypeScript中useFormTransform的精确类型约束问题
我完全理解你的需求——你希望transform参数里的每个转换函数,能精确接收对应属性的原始类型,而不是所有属性类型的联合(比如string | undefined | number)。原来的类型定义用了Partial<Record<keyof T, (value: T[keyof T]) => any>>,这里的问题在于T[keyof T]是所有属性类型的联合,所以每个转换函数的参数类型会被放宽到这个联合,而不是对应单个属性的精确类型。
咱们可以用TypeScript映射类型来解决这个问题,它能逐个遍历泛型T的键,并为每个键绑定对应的精确类型:
修正后的函数类型定义
const useFormTransform = <T>( formValues: T, transform: { // 遍历T的每个键K,定义可选的转换函数,参数类型精确为T[K] [K in keyof T]?: (value: T[K]) => any; } ) => { // 示例实现逻辑(你可以替换成自己的代码) return Object.fromEntries( Object.entries(formValues).map(([key, value]) => { const transformer = transform[key as keyof T]; return [key, transformer ? transformer(value as T[keyof T]) : value]; }) ) as { // 推导返回值类型:如果有转换函数则用其返回类型,否则保留原始类型 [K in keyof T]: (typeof transform)[K] extends undefined ? T[K] : ReturnType<(typeof transform)[K]> }; };
验证使用效果
现在再用你的示例代码测试,每个转换函数的参数类型会自动匹配对应属性的精确类型:
type Line = { id?: string; fromQuantity: number }; const line: Line = { id: 'abc', fromQuantity: 123 }; // 此时: // - id对应的转换函数参数f的类型是string | undefined // - fromQuantity对应的转换函数参数f的类型是number(完全匹配line.fromQuantity的类型) useFormTransform( line, { id: (f) => f, fromQuantity: (f) => transformNumber(f), // 这里f直接是number类型,无需再用Number(f)转换 }, );
关键原理说明
原来的Record<keyof T, ...>会把所有键的类型统一成keyof T,对应的参数类型也就变成了所有属性类型的联合。而映射类型[K in keyof T]?会逐个处理每个键K,并将函数参数类型绑定到T[K]——也就是该键对应的原始属性类型,这样就实现了精确的类型约束。
另外,上面的返回值类型推导也做了优化,会根据你是否提供了转换函数,自动推导最终返回对象的类型(转换后的类型或原始类型)。
内容的提问来源于stack exchange,提问作者Donovan BENFOUZARI
相关产品推荐
相关产品推荐

