Operator overloading for TypeScript.
boperators lets you define operator overloads (+, -, *=, ==, etc.) on your TypeScript classes. It works by transforming your source code at the AST level using ts-morph, replacing expressions like v1 + v2 with the corresponding overload call Vector3["+"](v1, v2).
class Vector3 {
public x: number;
public y: number;
public z: number;
// Static operator: takes two parameters, returns a new instance
static "+"(a: Vector3, b: Vector3): Vector3 {
return new Vector3(a.x + b.x, a.y + b.y, a.z + b.z);
}
// Instance operator: takes one parameter, mutates in place
"+="(rhs: Vector3): void {
this.x += rhs.x;
this.y += rhs.y;
this.z += rhs.z;
}
// ...
}
// Usage - these get transformed automatically:
const v3 = v1 + v2; // => Vector3["+"](v1, v2)
v1 += v2; // => v1["+="](v2)Overloads defined on a parent class are automatically inherited by subclasses. For example, if Expr defines + and *, a Sym extends Expr class can use those operators without redeclaring them.
A single operator can handle multiple type combinations using standard TypeScript method overload signatures. boperators registers each signature separately and dispatches to the correct one based on the operand types at each call site:
class Vec2 {
// Vec2 * Vec2 → component-wise multiplication
static "*"(a: Vec2, b: Vec2): Vec2;
// Vec2 * number → scalar multiplication
static "*"(a: Vec2, b: number): Vec2;
static "*"(a: Vec2, b: Vec2 | number): Vec2 {
if (b instanceof Vec2) return new Vec2(a.x * b.x, a.y * b.y);
return new Vec2(a.x * b, a.y * b);
}
}
const a = new Vec2(1, 2);
const b = new Vec2(3, 4);
a * b; // => Vec2["*"](a, b) → Vec2(3, 8) — routes to the Vec2 overload
a * 2; // => Vec2["*"](a, 2) → Vec2(2, 4) — routes to the number overloadThe implementation method must accept the union of all overload parameter types; only the overload signatures (those without a body) are registered in the overload store.
If you are publishing a package that exports classes with operator overloads, consumers need to be able to import those classes for the transformed code to work. Run the following before publishing to catch any missing exports:
# Level 1 — check every overload class is exported from its source file
boperate validate
# Level 2 — also verify each class is reachable via your package entry point
boperate validate --entry src/index.tsExit code is 1 on violations (suitable for CI / prepublish scripts). Use --warn to emit warnings instead of errors if you want a non-blocking check.
The validateExports function is also available in the core API for programmatic use:
import { validateExports } from "boperators";
const result = validateExports({ project, overloadStore, projectDir, entryPoint });
for (const v of result.violations) {
console.error(v.className, v.reason);
}| Package | Description |
|---|---|
boperators |
Core library - parses overload definitions and transforms expressions |
@boperators/cli |
CLI tool (bop / boperate) for batch transformation |
@boperators/plugin-bun |
Bun plugin - transforms files at runtime |
@boperators/plugin-ts-language-server |
TypeScript Language Server plugin - IDE support with source mapping |
@boperators/plugin-tsc |
ts-patch plugin - transforms during tsc compilation |
@boperators/webpack-loader |
Webpack loader - transforms files during webpack bundling |
@boperators/plugin-vite |
Vite plugin - transforms files during Vite/Rollup bundling |
@boperators/plugin-esbuild |
ESBuild plugin - transforms files during ESBuild bundling |
@boperators/mcp-server |
MCP server - gives AI assistants access to overload info, transform previews, and scaffolding |
See the example/ project for a working demo.
- Bun (runtime, package manager, and workspace tooling)
- Windows only: Developer Mode must be enabled (
Settings → System → For developers → Developer Mode). Bun uses symlinks to link local packages, which Windows requires elevated permission for.
git clone https://github.com/DiefBell/boperators.git
cd boperators
bun install# Build all packages
bun run build
# Build individual packages
bun run build:boperators
bun run build:cli
bun run build:ts-server-plugincd example
bun run transform # Transform source files to example/transformed/
bun run start # Run the transformed outputThis project uses BiomeJS for linting and formatting, with Husky + lint-staged for pre-commit hooks.
# Check for issues
bun run check
# Auto-fix issues
bun run check:write
# Format only
bun run formatboperators/
package/ Core library (boperators)
cli/ CLI tool (@boperators/cli)
mcp-server/ MCP server for agents e.g. Claude (@boperators/mcp-server)
plugins/
bun/ Bun plugin (@boperators/plugin-bun)
tsc/ ts-patch plugin (@boperators/plugin-tsc)
ts-language-server/ TS Language Server plugin
webpack/ Webpack loader (@boperators/webpack-loader)
vite/ Vite plugin (@boperators/plugin-vite)
esbuild/ ESBuild plugin (@boperators/plugin-esbuild)
examples/ Example projects
- Way better logging. Option to set a logger in the API i.e. for CLI, TS server, Bun plugin. Have log levels.
- Log function names when loading overloads. Mention in docs that named functions are preferred.
- TypeScript compiler plugin with ts-patch.
- Ensure classes correctly inherit the overloads of their parent class(es).
- Ensure that when trying to match a binary operation to its overload that we also look at the parents of each operand if they're child classes that may be compatible.
- Write tests, set up CI.
- MCP server for docs and tools. Allow viewing transformed for specific lines as well as whole file.
- Double check this
ts-morphdependency - can we keep it to only the core package? And put requiredtypescriptversion in a range? -
--projectto specify a TS config file for the CLI. - Expose a lot of config options in the core API, then implement a
.bopconf.jsonfor plugins and the CLI. - MCP server needs a README
- Fix Bun plugin
- Support unary operators
- Don't seem to be loading operators from libraries!
- Cli/tsc tool to check if a library is valid i.e. exports all classes with overloads.
- Fix CLI transformed output to match folder structure that tsc output would have
- Fix intellisense hovering
- Webpack plugin
- NextJS/Turbopack plugin handled by webpack one, technically it's a loader
- Vite plugin
- ESBuild plugin
- Add support for Mozilla / V3 source map format, use in webpack plugin.
-
Drop ts-morph dependency???NOPE - A lot of logic in plugins, like
transformerin thetscplugin, that could be unified in core. - Update main package's README for new plugins
- Sub-package for using Symbols
- e2e test for Bun plugin and tsc plugin
- ???
MIT
