Operator Overloading for JavaScript and TypeScript
npm install unplugin-op-overloadingOperator overloading for JavaScript and TypeScript using a build-time transformation.
- β¨ Natural Syntax - Write a + b instead of aSymbol.for('+')
- π§ Build-time Transformation - Zero runtime overhead, pure JavaScript output
- π¦ Universal - Works with Vite, Webpack, Rollup, esbuild, Rspack, and Rolldown
- π― Opt-in - Uses "use operator overloading" directive for explicit control
- π‘οΈ Type-safe - Includes TypeScript Language Service Plugin for IDE support
- β‘ Fast - Uses oxc-parser for blazing-fast AST parsing
``bash`
npm i -D unplugin-op-overloadingor
bun add -d unplugin-op-overloading
1. Add the plugin to your build tool:
`ts
// vite.config.ts
import { defineConfig } from 'vite'
import OperatorOverloading from 'unplugin-op-overloading/vite'
export default defineConfig({
plugins: [
OperatorOverloading({
equality: 'both', // If transforming ==, !=, ===, !==
}),
],
})
`
2. Add the directive to files using operator overloading:
`javascript
'use operator overloading'
class Vector {
constructor(x, y) {
this.x = x
this.y = y
}
Symbol.for('+') {
return new Vector(this.x + other.x, this.y + other.y)
}
}
const v1 = new Vector(3, 4)
const v2 = new Vector(1, 2)
const sum = v1 + v2 // Vector(4, 6) β¨
`
Vite
`ts
// vite.config.ts
import OperatorOverloading from 'unplugin-op-overloading/vite'
export default defineConfig({
plugins: [
OperatorOverloading({
equality: 'both',
debug: false,
}),
],
})
`
Rollup
`ts
// rollup.config.js
import OperatorOverloading from 'unplugin-op-overloading/rollup'
export default {
plugins: [
OperatorOverloading({
equality: 'both',
}),
],
}
`
Rolldown
`ts
// rolldown.config.js
import OperatorOverloading from 'unplugin-op-overloading/rolldown'
export default {
plugins: [
OperatorOverloading({
equality: 'both',
}),
],
}
`
esbuild
`ts
import { build } from 'esbuild'
import OperatorOverloading from 'unplugin-op-overloading/esbuild'
build({
plugins: [
OperatorOverloading({
equality: 'both',
}),
],
})
`
Webpack
`js
// webpack.config.js
import OperatorOverloading from 'unplugin-op-overloading/webpack'
export default {
plugins: [
OperatorOverloading({
equality: 'both',
}),
],
}
`
Rspack
`ts
// rspack.config.js
import OperatorOverloading from 'unplugin-op-overloading/rspack'
export default {
plugins: [
OperatorOverloading({
equality: 'both',
}),
],
}
`
| Option | Type | Default | Description |
| ---------- | ---------------------------------------- | --------------------- | ------------------------------------- |
| equality | 'off' \| 'loose' \| 'strict' \| 'both' | 'off' | Equality operator transformation mode |debug
| | boolean | false | Enable debug logging |include
| | FilterPattern | [/\.[cm]?[jt]sx?$/] | Files to include |exclude
| | FilterPattern | [/node_modules/] | Files to exclude |
- 'off' - Don't transform equality operators (default)'loose'
- - Transform == and !='strict'
- - Transform === and !=='both'
- - Transform all four equality operators
`javascript`
;Symbol.for('+') // a + b
Symbol.for('-') // a - b
Symbol.for('') // a b
Symbol.for('/') // a / b
Symbol.for('%') // a % b
Symbol.for('') // a b
`javascript`
;Symbol.for('<') // a < b
Symbol.for('>') // a > b
Symbol.for('<=') // a <= b
Symbol.for('>=') // a >= b
`javascript`
;Symbol.for('==') // a == b
Symbol.for('!=') // a != b
Symbol.for('===') // a === b
Symbol.for('!==') // a !== b
`javascript`
;[Symbol.for('minus')]() // -a
[Symbol.for('plus')]() // +a
`javascript
'use operator overloading'
class Vector {
constructor(x, y) {
this.x = x
this.y = y
}
Symbol.for('+') {
return new Vector(this.x + other.x, this.y + other.y)
}
Symbol.for('-') {
return new Vector(this.x - other.x, this.y - other.y)
}
Symbol.for('*') {
return new Vector(this.x scalar, this.y scalar)
}
[Symbol.for('minus')]() {
return new Vector(-this.x, -this.y)
}
magnitude() {
return Math.hypot(this.x, this.y)
}
Symbol.for('<') {
return this.magnitude() < other.magnitude()
}
}
const v1 = new Vector(3, 4)
const v2 = new Vector(1, 2)
const sum = v1 + v2 // Vector(4, 6)
const diff = v1 - v2 // Vector(2, 2)
const scaled = v1 * 2 // Vector(6, 8)
const negated = -v1 // Vector(-3, -4)
const isLess = v2 < v1 // true (magnitude 2.236 < 5)
const chained = v1 + v2 * 2 // Vector(5, 8)
`
`javascript
'use operator overloading'
class Complex {
constructor(real, imag) {
this.real = real
this.imag = imag
}
Symbol.for('+') {
return new Complex(this.real + other.real, this.imag + other.imag)
}
Symbol.for('*') {
return new Complex(
this.real other.real - this.imag other.imag,
this.real other.imag + this.imag other.real,
)
}
toString() {
const sign = this.imag >= 0 ? '+' : ''
return ${this.real}${sign}${this.imag}i
}
}
const c1 = new Complex(1, 2)
const c2 = new Complex(3, 4)
const sum = c1 + c2 // 4+6i
const product = c1 * c2 // -5+10i
`
`javascript
'use operator overloading'
function Matrix(rows) {
return {
rows, // [[a, b], [c, d]]
Symbol.for('*') {
// Row Γ Column multiplication
const result = [
[
this.rows[0][0] * other.rows[0][0] +
this.rows[0][1] * other.rows[1][0],
this.rows[0][0] * other.rows[0][1] +
this.rows[0][1] * other.rows[1][1],
],
[
this.rows[1][0] * other.rows[0][0] +
this.rows[1][1] * other.rows[1][0],
this.rows[1][0] * other.rows[0][1] +
this.rows[1][1] * other.rows[1][1],
],
]
return Matrix(result)
},
}
}
const A = Matrix([
[1, 2],
[3, 4],
])
const B = Matrix([
[5, 6],
[7, 8],
])
const C = A * B // Matrix([[19, 22], [43, 50]])
`
The plugin transforms operator expressions into method calls at build time:
Input:
`javascript`
'use operator overloading'
const sum = a + b
Output:
`javascript`
const sum = (() => {
'operator-overloading disabled'
const __lhs = a
const __rhs = b
const __sym = Symbol.for('+')
return __lhs != null && __lhs[__sym] !== undefined
? __lhs__sym
: __lhs + __rhs
})()
1. IIFE Wrapping - Each operator is wrapped in an immediately-invoked function expression to avoid variable conflicts
2. Fallback Behavior - Falls back to native JavaScript operators if no custom implementation exists
3. Null Safety - Checks for null/undefined before calling operator methods
4. Directive Removal - The "use operator overloading" directive is removed from output
This package includes two TypeScript plugins that work together to provide comprehensive operator overloading support:
Suppresses TypeScript errors in your editor for a clean development experience.
What it does:
- β
Removes red squiggles for operator overloading in VS Code, WebStorm, and other TypeScript-aware IDEs
- β
Works automatically with files containing "use operator overloading"@ts-expect-error
- β
No comments neededtsc
- β οΈ IDE-only: Does not affect compilation
Setup:
Add to your tsconfig.json:
`json`
{
"compilerOptions": {
"plugins": [{ "name": "unplugin-op-overloading/typescript-plugin" }]
}
}
VS Code Configuration:
Create .vscode/settings.json:
`json`
{
"typescript.tsdk": "node_modules/typescript/lib",
"typescript.enablePromptUseWorkspaceTsdk": true
}
Restart TypeScript server: Cmd/Ctrl + Shift + P β "TypeScript: Restart TS Server"
Transforms operator overloading syntax during TypeScript compilation.
What it does:
- β
Transforms code when using tsc directly
- β
Alternative to using build tool plugins (Vite/Webpack/Rollup)
- β
Useful for libraries or projects that rely on TypeScript's native compilation
- β
Enables operator overloading without bundler configuration
Setup:
Add to your tsconfig.json (in addition to the Language Service Plugin):
`json`
{
"compilerOptions": {
"plugins": [
{
"name": "unplugin-op-overloading/typescript-plugin"
},
{
"transform": "unplugin-op-overloading/typescript-plugin/transformer",
"transformProgram": true
}
]
}
}
For most projects using build tools (Vite/Webpack/Rollup/esbuild):
`json`
{
"compilerOptions": {
"plugins": [{ "name": "unplugin-op-overloading/typescript-plugin" }]
}
}
- Use the Language Service Plugin for IDE support
- Use the build tool plugin (configured separately) for actual transformation
- The Transformer Plugin is optional in this case
For projects using tsc directly (libraries, standalone TypeScript):
`json`
{
"compilerOptions": {
"plugins": [
{ "name": "unplugin-op-overloading/typescript-plugin" },
{
"transform": "unplugin-op-overloading/typescript-plugin/transformer",
"transformProgram": true
}
]
}
}
- Use both plugins for complete support
- Language Service Plugin provides IDE support
- Transformer Plugin handles code transformation during tsc compilation
β οΈ The Language Service Plugin only affects IDE experience. Running tsc directly will still show errors unless you also use the Transformer Plugin or a build tool plugin.
β
Recommended: Use build tools (Vite/Webpack/Rollup) instead of tsc for most projects. They provide better bundling, optimization, and full operator overloading support.
`typescript
'use operator overloading'
export class Vector {
constructor(
public x: number,
public y: number,
) {}
Symbol.for('+'): Vector {
return new Vector(this.x + other.x, this.y + other.y)
}
}
const v1 = new Vector(3, 4)
const v2 = new Vector(1, 2)
const sum = v1 + v2 // No IDE errors with TypeScript plugin!
`
See the complete working example in examples/vite-typescript/ featuring:
- β
Vector operations with 7 operators
- β
Complex number arithmetic
- β
2Γ2 Matrix multiplication
- β
Interactive frontend demo
- β
TypeScript Language Service Plugin configured
- β
Zero build warnings
`bash`
cd examples/vite-typescript
bun install
bun run dev
``
βββββββββββββββββββββββββββββββββββββββββββ
β Source Code (.js, .ts, .tsx) β
β 'use operator overloading' β
β const sum = a + b β
βββββββββββββββββββββββββββββββββββββββββββ
β
βββββββββββββββββββββββββββββββββββββββββββ
β unplugin-op-overloading β
β - Detects directive β
β - Parses with oxc-parser β
β - Transforms operators β
β - Generates sourcemaps β
βββββββββββββββββββββββββββββββββββββββββββ
β
βββββββββββββββββββββββββββββββββββββββββββ
β Transformed Output β
β const sum = (() => { β
β const __lhs = a, __rhs = b β
β return __lhs?.Symbol.for('+') β
β })() β
βββββββββββββββββββββββββββββββββββββββββββ
- β‘ Parsing: Uses oxc-parser - one of the fastest JavaScript parsers
- π― Selective: Only processes files with "use operator overloading" directive
- π¦ Zero Runtime: Pure compile-time transformation, no runtime library needed
- πΊοΈ Source Maps: Full source map support for debugging
1. Directive Required: Files must include "use operator overloading" at the top (within first 3 lines)tsc
2. TypeScript CLI: will still show errors - use build tools for production
3. Operator Precedence: Follows standard JavaScript operator precedence
4. Not a Language Feature: This is a build-time transformation, not native JavaScript
`bashRun all tests
bun run test
- TypeScript Plugin Guide
- Design Document
- Example Project
Apache-2.0 License Β© 2025-PRESENT Arafat Husayn