MST

星途 面试题库

面试题:TypeScript 中上下文类型推断与泛型的结合运用及边界情况处理

编写一个通用的函数 `mapValues`,它接收一个对象和一个回调函数,回调函数会作用于对象的每个值,并返回一个新的对象,新对象的键与原对象相同,值是经过回调函数处理后的结果。要求使用泛型来保证类型的正确性,并利用上下文类型推断使得调用 `mapValues` 函数时尽量减少类型声明。同时,考虑并处理可能出现的边界情况,例如对象为空、回调函数返回类型与原对象值类型不兼容等。请详细阐述实现思路以及上下文类型推断和泛型在其中所起的作用。
47.4万 热度难度
前端开发TypeScript

知识考点

AI 面试

面试题答案

一键面试
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>`,在编译阶段会进行类型检查,如果回调函数返回类型与期望不符会报错,从而保证类型安全。