编程全栈开发者

描述TypeScript中Spread和Rest运算符的机制,它们如何与严格类型化一起工作,以及在处理对象和数组时应该注意什么?

用 Hintsage AI 助手通过面试

答案。

问题历史

Spread(...)和Rest(...)是从ES6引入的现代语法构造,最受欢迎的之一。在TypeScript中,它们具有类型的严格性,因此Spread和Rest的使用不仅方便,而且安全,只要正确遵循类型限制。

问题

如果不考虑值的类型或混合不兼容的类型(例如,合并具有不兼容属性的对象或不同类型的数组),则Spread和Rest操作会成为错误的来源。

解决方案

在TypeScript中,Spread用于复制或合并对象的属性和数组的元素。Rest用于在函数或数组中收集“剩余”元素,在此过程中,严格的类型检查会检测不兼容的情况,防止在编译阶段出现错误。

代码示例:

// 对象的Spread const base = { a: 1, b: 2 }; const extended = { ...base, c: 3 }; // extended: { a: number; b: number; c: number } // 函数参数中的Rest function sum(...args: number[]): number { return args.reduce((acc, val) => acc + val, 0); } // 数组中的Spread const arr = [1, 2, 3]; const newArr = [...arr, 4, 5];

主要特点:

  • Spread确保复制属性/元素时检查类型的兼容性
  • Rest确保剩余参数的严格类型
  • 错误类型在不正确的合并时会在编译阶段就被捕获

具有陷阱的问题。

通过Spread复制对象与按引用赋值有什么不同?

Spread创建一个新对象,并复制所有可枚举的属性,但如果对象中有嵌套对象,它们是按引用复制的(浅拷贝)。

const base = { a: 1, nested: { x: 2 } }; const copy = { ...base }; copy.nested.x = 42; console.log(base.nested.x); // 42

可以使用Spread仅复制特定属性吗?

不可以,Spread复制对象的所有可枚举属性。要选择特定的属性,可以使用解构:

const { a, ...rest } = { a: 1, b: 2, c: 3 }; // rest: { b: 2, c: 3 }

如果通过Spread合并两个具有相同属性的对象会发生什么?这对类型检查有什么影响?

最后一个对象的属性会覆盖前一个对象的属性。同时,最后属性的类型将被视为最终类型:

const a = { val: 1 }; const b = { val: 'hello' }; const merged = { ...a, ...b }; // merged: { val: string }(而不是number)

类型错误和反模式

  • 使用Spread进行嵌套对象的深拷贝,尽管这只是“浅拷贝”
  • 对rest参数的错误类型化(使用any[]而不是具体的类型)
  • 合并具有重叠不兼容类型属性的对象

现实生活中的例子

负面案例

在项目中决定通过Spread合并配置参数。一个对象具有属性timeout: number,另一个对象具有timeout: string。在运行时之前没有注意到任何错误,直到函数因错误类型而失败。

优点:

  • 用几行快速实现

缺点:

  • 如果明确指定类型为any,类型错误不明显
  • 属性重叠的陷阱

正面案例

使用类型化的Spread合并严格接口,并使用解构来分离不需要的字段。编译器立即识别出错误。

优点:

  • 在不兼容时自动检查和突出显示错误
  • 安全的合并和修改类型

缺点:

  • 需要仔细设计接口