编程前端开发工程师

如何通过用户方法在 TypeScript 中实现类型化运算符重载机制?为什么不能直接重载运算符,如何模拟这种功能?

用 Hintsage AI 助手通过面试

答案。

问题的背景

许多严格类型的语言(如 C++、C#、Python)允许在类级别显式描述运算符(例如 +、-、*、==)的重载。在 TypeScript 和 JavaScript 中,由于历史和架构原因,语言级别上缺乏运算符重载。

问题

当需要为复杂类型(例如复数或向量)添加算术或其他操作的支持时,无法更改标准运算符。这限制了表达能力,要求我们发明绕过的模式。

解决方案

在 TypeScript 中,通过专门命名的方法(例如 add、equals、multiply)来模拟运算符重载。通过为类提供这些方法,我们实现了类型安全的行为,尽管语法看起来没有 C++ 那么简洁。

代码示例:

class Vector { constructor(public x: number, public y: number) {} add(other: Vector): Vector { return new Vector(this.x + other.x, this.y + other.y); } equals(other: Vector): boolean { return this.x === other.x && this.y === other.y; } } const a = new Vector(1, 2); const b = new Vector(3, 4); const c = a.add(b); // Vector { x: 4, y: 6 } const eq = a.equals(b); // false

关键特点:

  • 语法层面无法进行运算符重载
  • 使用明显的命名方法(例如 add、sub、equals)
  • 方法的类型化确保了操作的安全性

误导性的问题。

可以在 TypeScript 中重写"=="或"+"运算符的行为吗?

不可以,JavaScript(和 TypeScript)不允许直接重载用户对象的标准运算符。运算符严格按照 Object、Number 和其他原始类型的标准进行应用。任何此类“重写”的尝试都会导致不正确或意外的行为,或者直接不工作。

可以使用 valueOf 或 toString 来间接影响运算符吗?

在某些情况下,可以使用 valueOf(例如,进行简单的数值转换),但仅限于那些调用对象原始值的运算符。

class Box { constructor(private v: number) {} valueOf() { return this.v; } } const a = new Box(10); console.log(a + 5); // 15 — 有效,但对于复杂对象来说不是直观的

但是对于复杂结构和逻辑运算符,显式的方法更为合适。

TypeScript 或 ECMAScript 中是否有添加运算符重载的计划?

目前没有确认的计划:原则上,运算符重载与 JS 的设计相悖,因为它可能会显著改变标准对象的行为并导致代码的不稳定。

常见错误和反模式

  • 尝试通过 valueOf/toString 重写标准运算符的行为,用于复杂结构
  • 使用不具体的名称的方法(例如 op、act,而不是 add、equals)
  • 方法类型化不足或用户方法签名不正确

现实中的示例

负面案例

开发人员创建一个 Matrix 类,在其中实现 valueOf/toString 以插入数学表达式。与数字或字符串相加时,得到了不可预测的结果,无法进行严格的类型化。

优点:

  • 操作的语法更加简洁

缺点:

  • 失去了类型安全性和明显性
  • 在与其他对象共同使用时表现模糊

积极案例

Vector 类实现了 add、multiply、equals 方法,并明确了输入和输出参数的类型。类的客户端明确使用相应的方法,语义不会丢失,并确保了严格的类型化。

优点:

  • 可预测的行为
  • 易于扩展
  • 支持严格的类型化

缺点:

  • 语法比运算符“本地化”差
  • 不支持重写为习惯的算术语法