@lopatnov/join
v2.1.0
Published
SQL-like join operations for JavaScript objects — merge two objects using 9 composable join types (left, right, inner, full and more) with deep merge support.
Maintainers
Readme
@lopatnov/join
A TypeScript library providing SQL-like join operations for JavaScript objects. Merge two objects using one of 9 composable join types, with support for deep merging of nested objects.
Table of Contents
Installation
npm install @lopatnov/joinBrowser:
<script src="https://lopatnov.github.io/join/dist/join.umd.min.js"></script>Import
TypeScript / ES Modules
import { join, JoinTypes } from "@lopatnov/join";CommonJS
const { join, JoinTypes } = require("@lopatnov/join");Join Types

The library uses a 4-bit flag system to describe which parts of two objects appear in the result:
| Bit | Name | Meaning |
| :-: | ------------ | ---------------------------------------------- |
| 3 | left | Include properties unique to the left object |
| 2 | innerLeft | Include shared properties, keeping left values |
| 1 | innerRight | Include shared properties, using right values |
| 0 | right | Include properties unique to the right object |
Named Join Types
| Join Type | Bits | Description |
| :----------- | :----: | ---------------------------------------------------------------- |
| none | 0000 | Empty result — no properties from either object |
| left | 1000 | Only properties unique to the left object |
| right | 0001 | Only properties unique to the right object |
| innerLeft | 0100 | Shared properties only, keeping left values |
| innerRight | 0010 | Shared properties only, replacing with right values |
| innerJoin | 0110 | Shared properties, deep-merged (left + right values combined) |
| leftJoin | 1110 | Left-unique + shared properties (deep-merged) |
| rightJoin | 0111 | Shared properties (deep-merged) + right-unique |
| fullJoin | 1111 | All properties from both objects, shared ones deep-merged |
| expand | 1011 | Left-unique + right values for shared + right-unique (default) |
Custom join types can be composed with bitwise OR:
const customJoin = join(JoinTypes.left | JoinTypes.innerLeft | JoinTypes.right);API
function join(
joinType?: JoinTypes
): <TContext>(
context: TContext
) => <TJoinObject>(joinObject: TJoinObject) => TContext & TJoinObject;join uses a curried three-step pattern:
| Step | Call | Description |
| ---- | --------------------------- | ---------------------------------------------------- |
| 1 | join(JoinTypes.xxx) | Set the join type; returns a context-setter function |
| 2 | contextSetter(leftObject) | Set the left object; returns a joiner function |
| 3 | joiner(rightObject) | Set the right object; returns the merged result |
JoinTypes.expand is the default join type when join() is called with no arguments.
Usage Examples
Right Join
Returns only the properties unique to the right object:
const rightJoin = join(JoinTypes.right);
const contextJoinBy = rightJoin({
sample1: "One",
sample2: "Two",
sample3: "Three"
});
const result = contextJoinBy({
sample2: "Dos",
sample3: "Tres",
sample4: "Quatro"
});
console.log(result); // { sample4: "Quatro" }Left Join
Returns only the properties unique to the left object:
const leftJoin = join(JoinTypes.left);
const contextJoinBy = leftJoin({
sample1: "One",
sample2: "Two",
sample3: "Three"
});
const result = contextJoinBy({
sample2: "Dos",
sample3: "Tres",
sample4: "Quatro"
});
console.log(result); // { sample1: "One" }Custom Bitwise Composition
Returns left-unique + shared (from left) + right-unique properties:
const customJoin = join(JoinTypes.left | JoinTypes.innerLeft | JoinTypes.right);
const contextJoinBy = customJoin({
sample1: "One",
sample2: "Two",
sample3: "Three"
});
const result = contextJoinBy({
sample2: "Dos",
sample3: "Tres",
sample4: "Quatro"
});
console.log(result); // { sample1: "One", sample2: "Two", sample3: "Three", sample4: "Quatro" }Inner Join (Deep Merge)
Returns shared properties with their values deep-merged:
const result = join(JoinTypes.innerJoin)({
sample1: "One",
sample2: "Two",
sample3: { smile: "cheese" }
})({
sample2: "Dos",
sample3: { sorrir: "queijo" },
sample4: "Quatro"
});
console.log(result);
// { sample2: "Dos", sample3: { smile: "cheese", sorrir: "queijo" } }Demo
- Live demo: https://runkit.com/lopatnov/join
- Try in browser: https://npm.runkit.com/@lopatnov/join
Contributing
Contributions are welcome! Please read CONTRIBUTING.md before opening a pull request.
- Bug reports → open an issue
- Questions → Discussions
- Found it useful? A star on GitHub helps others discover the project
Built With
- TypeScript — strict typing throughout
- Rollup — bundled to ESM, CJS, and UMD formats
- Vitest — unit testing with coverage
- ESLint + Prettier — linting and formatting
License
Apache-2.0 © 2020–2026 Oleksandr Lopatnov · LinkedIn
