An open API service indexing awesome lists of open source software.

https://github.com/arafathusayn/unplugin-op-overloading

Operator overloading for JavaScript and TypeScript
https://github.com/arafathusayn/unplugin-op-overloading

javascript operator-overloading typescript

Last synced: about 1 month ago
JSON representation

Operator overloading for JavaScript and TypeScript

Awesome Lists containing this project

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

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

## Usage

### Quick Start

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('+')](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

```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',
}),
],
}
```


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

```javascript
;[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

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

### Equality Operators

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

### Unary Operators

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

## Examples

### Vector Mathematics

```javascript
'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

```javascript
'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

```javascript
'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:**

```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](__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`:

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

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

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

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

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

```typescript
'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/`](./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
```

## 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](https://github.com/oxc-project/oxc) - 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

```bash
# Run all tests
bun run test

# Type checking
bun run typecheck

# Linting
bun run lint

# Build
bun run build
```

## Documentation

- [TypeScript Plugin Guide](./docs/typescript-plugin-guide.md)
- [Design Document](./docs/design.md)
- [Example Project](./examples/vite-typescript/)

## License

[Apache-2.0](./LICENSE) License Β© 2025-PRESENT [Arafat Husayn](https://github.com/arafathusayn)