npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

unplugin-op-overloading

v0.0.6

Published

Operator Overloading for JavaScript and TypeScript

Readme

Operator overloading for JavaScript and TypeScript using a build-time transformation.

Features

  • Natural Syntax - Write a + b instead of a[Symbol.for('+')](b)
  • 🔧 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

Installation

npm i -D unplugin-op-overloading
# or
bun add -d unplugin-op-overloading

Usage

Quick Start

  1. Add the plugin to your build tool:
// vite.config.ts
import { defineConfig } from 'vite'
import OperatorOverloading from 'unplugin-op-overloading/vite'

export default defineConfig({
  plugins: [
    OperatorOverloading({
      equality: 'both', // If transforming ==, !=, ===, !==
    }),
  ],
})
  1. Add the directive to files using operator overloading:
'use operator overloading'

class Vector {
  constructor(x, y) {
    this.x = x
    this.y = y
  }

  [Symbol.for('+')](other) {
    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) ✨

Build Tool Configuration

// vite.config.ts
import OperatorOverloading from 'unplugin-op-overloading/vite'

export default defineConfig({
  plugins: [
    OperatorOverloading({
      equality: 'both',
      debug: false,
    }),
  ],
})

// rollup.config.js
import OperatorOverloading from 'unplugin-op-overloading/rollup'

export default {
  plugins: [
    OperatorOverloading({
      equality: 'both',
    }),
  ],
}

// rolldown.config.js
import OperatorOverloading from 'unplugin-op-overloading/rolldown'

export default {
  plugins: [
    OperatorOverloading({
      equality: 'both',
    }),
  ],
}

import { build } from 'esbuild'
import OperatorOverloading from 'unplugin-op-overloading/esbuild'

build({
  plugins: [
    OperatorOverloading({
      equality: 'both',
    }),
  ],
})

// webpack.config.js
import OperatorOverloading from 'unplugin-op-overloading/webpack'

export default {
  plugins: [
    OperatorOverloading({
      equality: 'both',
    }),
  ],
}

// rspack.config.js
import OperatorOverloading from 'unplugin-op-overloading/rspack'

export default {
  plugins: [
    OperatorOverloading({
      equality: 'both',
    }),
  ],
}

Plugin Options

| 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 |

Equality Modes

  • 'off' - Don't transform equality operators (default)
  • 'loose' - Transform == and !=
  • 'strict' - Transform === and !==
  • 'both' - Transform all four equality operators

Supported Operators

Arithmetic Operators

;[Symbol.for('+')](other) // a + b
  [Symbol.for('-')](other) // a - b
  [Symbol.for('*')](other) // a * b
  [Symbol.for('/')](other) // a / b
  [Symbol.for('%')](other) // a % b
  [Symbol.for('**')](other) // a ** b

Comparison Operators

;[Symbol.for('<')](other) // a < b
  [Symbol.for('>')](other) // a > b
  [Symbol.for('<=')](other) // a <= b
  [Symbol.for('>=')](other) // a >= b

Equality Operators

;[Symbol.for('==')](other) // a == b
  [Symbol.for('!=')](other) // a != b
  [Symbol.for('===')](other) // a === b
  [Symbol.for('!==')](other) // a !== b

Unary Operators

;[Symbol.for('minus')]() // -a
  [Symbol.for('plus')]() // +a

Examples

Vector Mathematics

'use operator overloading'

class Vector {
  constructor(x, y) {
    this.x = x
    this.y = y
  }

  [Symbol.for('+')](other) {
    return new Vector(this.x + other.x, this.y + other.y)
  }

  [Symbol.for('-')](other) {
    return new Vector(this.x - other.x, this.y - other.y)
  }

  [Symbol.for('*')](scalar) {
    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('<')](other) {
    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)

Complex Numbers

'use operator overloading'

class Complex {
  constructor(real, imag) {
    this.real = real
    this.imag = imag
  }

  [Symbol.for('+')](other) {
    return new Complex(this.real + other.real, this.imag + other.imag)
  }

  [Symbol.for('*')](other) {
    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

Matrix Multiplication

'use operator overloading'

function Matrix(rows) {
  return {
    rows, // [[a, b], [c, d]]

    [Symbol.for('*')](other) {
      // 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]])

How It Works

Transformation Process

The plugin transforms operator expressions into method calls at build time:

Input:

'use operator overloading'
const sum = a + b

Output:

const sum = (() => {
  'operator-overloading disabled'
  const __lhs = a
  const __rhs = b
  const __sym = Symbol.for('+')
  return __lhs != null && __lhs[__sym] !== undefined
    ? __lhs[__sym](__rhs)
    : __lhs + __rhs
})()

Key Features

  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

TypeScript Support

This package includes two TypeScript plugins that work together to provide comprehensive operator overloading support:

1. Language Service Plugin (IDE 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"
  • ✅ No @ts-expect-error comments needed
  • ⚠️ IDE-only: Does not affect tsc compilation

Setup:

Add to your tsconfig.json:

{
  "compilerOptions": {
    "plugins": [{ "name": "unplugin-op-overloading/typescript-plugin" }]
  }
}

VS Code Configuration:

Create .vscode/settings.json:

{
  "typescript.tsdk": "node_modules/typescript/lib",
  "typescript.enablePromptUseWorkspaceTsdk": true
}

Restart TypeScript server: Cmd/Ctrl + Shift + P → "TypeScript: Restart TS Server"

2. TypeScript Transformer Plugin (Compile-time Transformation)

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):

{
  "compilerOptions": {
    "plugins": [
      {
        "name": "unplugin-op-overloading/typescript-plugin"
      },
      {
        "transform": "unplugin-op-overloading/typescript-plugin/transformer",
        "transformProgram": true
      }
    ]
  }
}

Which Plugin Configuration Should I Use?

For most projects using build tools (Vite/Webpack/Rollup/esbuild):

{
  "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):

{
  "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

Important Notes

⚠️ 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 Example

'use operator overloading'

export class Vector {
  constructor(
    public x: number,
    public y: number,
  ) {}

  [Symbol.for('+')](other: Vector): 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!

Working Example

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
cd examples/vite-typescript
bun install
bun run dev

Architecture

┌─────────────────────────────────────────┐
│  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('+')](...)  │
│  })()                                   │
└─────────────────────────────────────────┘

Performance

  • 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

Limitations

  1. Directive Required: Files must include "use operator overloading" at the top (within first 3 lines)
  2. TypeScript CLI: tsc 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

Testing

# Run all tests
bun run test

# Type checking
bun run typecheck

# Linting
bun run lint

# Build
bun run build

Documentation

License

Apache-2.0 License © 2025-PRESENT Arafat Husayn