Use cases
The use/need for custom operators behaviour is somewhat inspired by C++'s ability to use custom operators on objects. This new system would use Symbol
types to add well-known properties to objects and/or functions which would allow defining custom behaviour for objects, when operators are used on them.
One of dozens of potential use cases would be a Vector
class. Consider the following example:
(I'm using TypeScript notation here for clarity)
class Vector {
constructor(public x: number, public y: number) {
// ...
}
add(vector: Vector): Vector {
// Vector addition code
}
}
let position = new Vector(10, 10);
const velocity = new Vector(1, 0);
position = position.add(velocity);
The above example calls the .add
function to combine the values of two vector types, to produce another vector.
If the Vector
class were instead defined like this
class Vector {
// ...
[Symbol.Plus](vector: Vector): Vector {
return this.add(vector);
}
}
then one could simply use the addition operator to perform the same action: position = position + velocity
, therefore position += velocity
. This would significantly improve code readability, as well as increasing the use-case for operators, as most operations are limited to primitive types.
Naturally, this would not have to be applicable to every object. For instance this would not make sense with Promise
objects, but for scenarios, where custom behaviour can be used, this would allow the programmer to use syntax which more closely represents the algorithmic notation.
Potential points of friction.
Incompatible Object Types
If an object is operated upon by an object of different types, then undefined behaviour can occur. To mitigate this, it would be a TypeError
if the second operand does not inherit from the first.
In the case where operator functions are defined on inline objects, any parameter is allowed, and simply relies on the operator function to determine the correct object type, throwing errors in the case of incompatible types.
Functions
Operators can be defined on functions using Object.defineProperty
and the like, or by defining them on the function's this
context, depending on how it was called.
Accidental (or deliberate) security holes
The following scenario shows how an attacker might use this functionality to produce a calling-side code execution system:
// Some library
function diffBetweenDates(date1: Date, date2: Date) {
return date2 - date1;
}
// Attacker
const maliciousDate = {
...new Date(),
[Symbol.Minus](date2: Date): Date {
// Malicious code
}
};
diffBetweenDates(harmlessDateObject, maliciousDate);
If instead the maliciousDate
was created from a function with sensitive data in its context, the context of the operator's context may be linked to it, exposing it. I'm not certain how/if this would even become a serious issue, and what to do about it, if it were, however I think it noteworthy of mention here.