observable-callback
A tiny RxJS wrapper that turns any callback into an observable stream and its trigger function.
Repository Health
Technical Analysis
observable-callback is a small TypeScript utility that wraps an RxJS Subject behind a single factory function. Calling observableCallback() returns a tuple: an Observable you can subscribe to, and a plain function that pushes values onto that stream when invoked. It exists for the common case where the only way to know an event happened is by handing some API a function to call later — turning that inversion-of-control pattern into a first-class observable stream instead.
The library is deliberately minimal: no dependencies of its own, a single ~20-line source file, and a peer dependency on RxJS 6 or 7. An optional operator function can be passed in to transform the stream inline (mapping, filtering, combining with other sources) with full type inference flowing from the operator’s input and output types through to the returned observable and callback signatures.
What You Get
- A single
observableCallback()factory that returns[Observable, callback]— no manualSubjectbookkeeping - Full TypeScript overloads: no-arg (void stream), generic type-parameter, or operator-function forms, each with accurate inferred types
- Optional operator-function argument so the emitted stream can be mapped, filtered, or combined inline as it’s created
- Zero runtime dependencies — only a peer dependency on RxJS 6.5+ or 7.x, so it adds negligible weight to a project already using RxJS
Common Use Cases
- Turning a DOM/React event handler prop (e.g.
onClick) into an observable stream to compose with RxJS operators - Exposing an imperative “dispatch” function alongside a reactive stream of dispatched values, e.g. a router’s
navigate()pluscurrentPage$ - Bridging third-party APIs that only accept a callback into an observable-first codebase
- Building simple pub/sub action streams without wiring up a full state-management library
Under The Hood
Architecture
The entire library is one function in one file (observableCallback.ts, ~17 lines): it constructs an RxJS Subject, optionally pipes it through a caller-supplied operator, and returns a tuple of the resulting observable and a bound next() call as the callback. There are no internal layers, no state beyond the Subject itself, and nothing else in the codebase depends on or extends this abstraction — changing it only affects the one exported function’s signature and behavior.
Tech Stack
Written in TypeScript, built three ways from the same source (tsc targeting ES2015, ES5-as-ESM, and ES5-as-CommonJS) via separate npm run build:* scripts with no bundler involved, and tested with Jest through ts-jest. RxJS is the sole peer dependency (^6.5 || ^7), and the package ships type declarations generated straight from the TypeScript source. There is no CI configuration in the repository; publishing runs the build via a prepublishOnly hook.
Code Quality
A runtime test file (observableCallback.test.ts) covers the two call shapes (no-arg and operator-argument) with Jest assertions, and a separate typings.test.ts performs compile-time-only type testing using @ts-expect-error comments, relying on tsc to fail the build if the overloaded generic signatures ever accept the wrong argument types. There’s no linter configuration beyond Prettier for formatting, and no CI workflow to run tests automatically on push. Naming is consistent and the code leans heavily on TypeScript generics and function overloads rather than runtime checks, so most “error handling” is pushed to compile time.
API Design
The public surface is a single function with three overloaded call signatures, returning a plain two-element tuple that mirrors the now-familiar [value, setValue] destructuring pattern from React hooks — there’s no configuration object, no class to instantiate, and no setup beyond the one import. The README backs this with four runnable examples covering the base case, an operator-transformed case, a React event-handler integration, and a router/action-dispatch pattern, which is a lot of applied guidance for a library this small.