Macaroni
Operator overloading for javascript!
Example
; ;; ;console.logclassC instanceof MyClass; // expected output: trueconsole.logclassC.prop; // expected output: 5 ;console.logmanualAddition instanceof MyClass; // again, trueconsole.logmanualAddition.prop; // I think you can figure this one out
Usage
- Install macaroni
- Install your plugin of choice to transform the code into macaroni-able code (e.g. babel-plugin-macaroni)
- Run it!
Caveats
- Type hinting is currently unsolved
- Requires a 3rd-party transformer unless you use
Operator
directly, which would be pretty weird - Impossible to override behavior in engine classes such as
Set
,Map
-- an "override" class is necessary for such behavior. 3 + instance
is not defined behavior at the current time, it currently relies on you to implement[Symbol.toPrimitive]
- Requires symbol polyfill if being used in a browser context
Supported Operators
To use these operators, import operators
from this package (e.g. import { operators } from 'macaroni'
) and use a computed property for operators.$OPERATOR_NAME
.
Notes:
- Where the primitive equivalent contains an "a" and a "b", the operator will be called with an additional "b" argument ("a" should be accessible from "this"). When there is no "b", no parameters will be passed.
- There is no "notEqual" of unsafe/strict, because it is inferred that notEqual is the opposite of equal.
Operator | Primitive Equivalent | Notes |
---|---|---|
add | a + b | |
subtract | a - b | |
multiply | a * b | |
divide | a / b | |
pow | a ** b | Equivalent to Math.pow(a, b) as well |
mod | a % b | |
lessThan | a < b | |
lessEqual | a <= b | |
greaterThan | a > b | |
greaterEqual | a >= b | |
unsafeEqual | a == b | Advised against, hence the unsafe prefix |
strictEqual | a === b | Use this one instead! |
logicalAnd | a & b | |
logicalOr | a | b | |
logicalXor | a ^ b | |
logicalNot | ~a | |
leftShift | a << b | |
rightShift | a >> b | |
increment | ++a or a++ | Prefix and postfix are preserved, so behavior should be as expected for these cases. |
decrement | --a or a++ | See above note |
negate | -a | |
positive | +a | |
not | !a | |
getProperty | a[prop] | Requires capturePropertyAccess. Function signature is [operator.getProperty](prop): void |
setProperty | a[prop] = val | Requires capturePropertyAccess. Function signature is [operator.setProperty](prop, val): boolean . You MUST return true in strict mode if you don't want to throw an error. If you're in sloppy mode, follow your dreams. |
Get/Set Traps
As you can see above, getProperty and setProperty are "supported". The only way for them to work is through ES6 Proxies, so you must wrap your class in a call to a helper method to use an ES6 Proxy for such a case.
Some notes:
- As mentioned above, you should probably return a bool in your setProperty trap if you don't want errors in strict mode (must return true for no error)
- This will probably significantly decrease performance for classes expecting a lot of property accesses/sets, please check benchmarks for ES6 proxies.
- set and get traps are both registered and will be called if they ever exist, so you may dynamically add/remove the overloads if you so wish (similarly to regular classes and such).
- If you only use get/set behavior, babel is not needed (it uses an ES6 Proxy only, which does not require )
Usage
Note that this method can take a constructor (class declaration), an existing class instance, or a plain JS object.
; // method 1 { // ... } TrapClassExport; // method 2 (no change in functionality of course) { // ... }; // Can be used on objects...const trapObject = ; // ...or instantiated classesconst trapInstance = ;
Future Roadmap
- Manual hash table implementation
- Necessary for OperatorMap, OperatorSet, OperatorArray
- Should come with
operators.hash
- Better TypeScript support
- Decorators for get/set traps (instead of the kind of messy capturePropertyAccess method)