function mapValues<O extends Record<string, any>, F extends (value: O[keyof O], key: keyof O) => any>(
obj: O,
callback: F
): { [K in keyof O]: ReturnType<F> } {
const result: any = {};
for (const key in obj) {
if (Object.prototype.hasOwnProperty.call(obj, key)) {
result[key] = callback(obj[key], key);
}
}
return result;
}
// 实现思路:
// 1. 定义泛型 `O` 代表输入的对象类型,约束为 `Record<string, any>`,表示它是一个键为字符串,值为任意类型的对象。
// 2. 定义泛型 `F` 代表回调函数类型,它接收对象的值和键作为参数,并返回任意类型。
// 3. 使用 `{ [K in keyof O]: ReturnType<F> }` 来定义返回对象的类型,键与原对象 `O` 相同,值的类型是回调函数 `F` 的返回类型。
// 4. 在函数内部,通过 `for...in` 循环遍历对象的键,并使用 `Object.prototype.hasOwnProperty.call` 来确保该键是对象自身的属性而非原型链上的属性。
// 5. 对每个值应用回调函数,并将结果存储在新对象中。
// 上下文类型推断的作用:
// 当调用 `mapValues` 函数时,TypeScript 可以根据传入的对象和回调函数的实际类型,自动推断出泛型 `O` 和 `F` 的具体类型,从而减少手动声明类型的麻烦。例如:
// const obj = { a: 1, b: 2 };
// const newObj = mapValues(obj, (value) => value * 2);
// 这里 TypeScript 可以推断出 `O` 为 `{ a: number; b: number }`,`F` 为 `(value: number) => number`。
// 泛型的作用:
// 1. 保证类型的正确性,通过泛型 `O` 来约束输入对象的类型,通过泛型 `F` 来约束回调函数的类型,以及返回对象的类型,确保整个操作在类型安全的环境下进行。
// 2. 提高代码的复用性,`mapValues` 函数可以适用于不同类型的对象和回调函数,而不需要为每种具体类型都编写一个单独的函数。
// 边界情况处理:
// 1. 对象为空:当对象为空时,`for...in` 循环不会执行,直接返回一个空对象,符合预期。
// 2. 回调函数返回类型与原对象值类型不兼容:通过泛型 `F` 定义回调函数返回类型,以及返回对象值类型为 `ReturnType<F>`,在编译阶段会进行类型检查,如果回调函数返回类型与期望不符会报错,从而保证类型安全。