Type-safe Promise-based Web Worker communication with TypeScript, supporting tagged messages and transferable objects
npm install @mr.python/promise-worker-tsType-safe Promise-based communication with Web Workers in TypeScript.


- 🔒 Fully Type-Safe: Complete end-to-end type safety for Web Worker communication
- 🏷️ Tagged Messages: Support for multiple message types in a single worker
- 🔄 Promise-Based: Clean, modern async/await syntax
- 📦 Zero Dependencies: Lightweight and framework-agnostic
- 🛡️ TypeScript First: Built with TypeScript for the best developer experience
- 📤 Transferable Objects: Full support for transferring ownership of objects between threads
``bash`
npm install promise-worker-tsor
yarn add promise-worker-tsor
pnpm add promise-worker-ts
1. Define your shared types (e.g., types.shared.ts):
`typescript
import type { PromiseWorker } from "promise-worker-ts";
export type DoubleNumber = PromiseWorker
`
2. Set up your worker (worker.ts):
`typescript
import { listen } from "promise-worker-ts";
import type { DoubleNumber } from "./types.shared";
listen
`
3. Use in your main thread:
`typescript
import { send } from "promise-worker-ts";
import type { DoubleNumber } from "./types.shared";
const worker = new Worker(/ ... /);
const result = await send
`
When you need multiple message types in a single worker:
1. Define tagged types:
`typescript
import type { PromiseWorkerTagged } from "promise-worker-ts";
export type AddNumbers = PromiseWorkerTagged<"add", [number, number], number>;
export type MultiplyNumbers = PromiseWorkerTagged<
"multiply",
[number, number],
number
>;
`
2. Set up worker handlers:
`typescript
import { listen } from "promise-worker-ts";
import type { AddNumbers, MultiplyNumbers } from "./types.shared";
listen
listen
`
3. Use in main thread:
`typescript
import { send } from "promise-worker-ts";
import type { AddNumbers, MultiplyNumbers } from "./types.shared";
const worker = new Worker("worker.ts");
const sum = await send
const product = await send
`
These types are technically objects, but they're convenience types for being able to define once and use in both workers and main thread.
#### PromiseWorker
Basic type for single-purpose workers
- Input: Type of data sent to workerOutput
- : Type of data received from workerError
- : Optional error type
#### PromiseWorkerTagged
Type for tagged messages in multi-purpose workers
- Tag: Literal string type for message identificationInput
- : Type of data sent to workerOutput
- : Type of data received from workerError
- : Optional error type
#### send
Send message to untagged worker
#### send
Send message to tagged worker
#### listen
Listen for untagged messages
#### listen
Listen for tagged messages
#### Reverse direction (worker → main)
- sendToMain
These mirror send/listen but in the opposite direction: call from the worker, handle on the main thread, with full support for tagging and transferables.
`typescript
type DivideNumbers = PromiseWorkerTagged<
"divide", // Tag
[number, number], // Input
number, // Output
"Division by zero" // Error
>;
// In worker
listen
if (b === 0) throw "Division by zero";
return a / b;
});
// In main thread
try {
const result = await send
} catch (error) {
console.error(error); // 'Division by zero'
}
`
`typescript
// Send with transferable objects
const arrayBuffer = new ArrayBuffer(1024);
await send
// Send back transferable objects
listen
const newArrayBuffer = new ArrayBuffer(1024);
// Add to transfer queue and return
transferables.push(newArrayBuffer);
return newArrayBuffer;
});
`
For listening, you can pass a callback function.
- [x] Write tests for transferable objects
- [ ] Write more robust test for passing _back_ transfer objects
- [x] Make it so workers can initiate promises to their parent threads
1. Define the types (shared):
`ts
import type { PromiseWorkerTagged } from "promise-worker-ts";
export type RevCallSquare = PromiseWorkerTagged<"rev.call.square", number, number>;
export type RevTriggerSquare = PromiseWorkerTagged<
"rev.trigger.square",
number,
number
>;
`
2. In worker:
`ts
import { listen, sendToMain } from "promise-worker-ts";
import type { RevCallSquare, RevTriggerSquare } from "./types.shared";
listen
return await sendToMain
});
`
3. In main:
`ts
import { listenMain, send } from "promise-worker-ts";
import type { RevCallSquare, RevTriggerSquare } from "./types.shared";
const worker = new Worker("reverse.worker.ts");
const stop = listenMain
const result = await send
// result === 10
stop();
`
Contributions are welcome! Please feel free to submit a Pull Request. For major changes, please open an issue first to discuss what you would like to change.
1. Fork the repository
2. Create your feature branch (git checkout -b feature/AmazingFeature)git commit -m 'Add some AmazingFeature'
3. Commit your changes ()git push origin feature/AmazingFeature`)
4. Push to the branch (
5. Open a Pull Request
This project is licensed under the MIT License - see the LICENSE file for details.
If you find this project helpful, please consider:
- Starring the repository ⭐
- Reporting issues 🐛
- Contributing improvements 🛠️
- Sharing with others 🌟
Created and maintained by Peter Batory-Bernardin