npm.io
0.2.0 • Published 2 weeks ago

@rafikidota/monadia

Licence
MIT
Version
0.2.0
Deps
0
Size
111 kB
Vulns
0
Weekly
0

@rafikidota/monadia

Sometimes, the best way to solve your own problems is to help someone else.

Monads for TypeScript. Zero dependencies, dual CJS/ESM.

npm install @rafikidota/monadia

ResultSync

Holds either a value R or an error E — never both, never neither.

import { ResultSync } from '@rafikidota/monadia';

function parse(raw: string): ResultSync<number, RangeError> {
  const port = Number(raw);
  if (!Number.isInteger(port) || port < 1 || port > 65535) {
    return ResultSync.Fail(new RangeError(`invalid port: ${raw}`));
  }
  return ResultSync.Ok(port);
}

parse('8080')
  .map((port) => `http://localhost:${port}`)
  .unwrapOr('http://localhost:3000');

ResultSync.Ok() with no argument is ResultSync<void, E>. The class is final: the constructor is private and instances are frozen. To extend behaviour, wrap it. private is a compile-time guarantee, so the constructor also validates its state at runtime for JavaScript consumers.

Group Members
Construction Ok, Fail (static)
State successful
Access getValue, getError
Transform map, mapOr, mapOrElse, mapOrError
Compose and, andThen, or, orElse
Extract expect, unwrap, unwrapOr, unwrapOrElse, unwrapError

ResultAsync

Wraps a Promise<ResultSync<R, E>>. Combinators return a new ResultAsync, so a chain reads as one expression.

import { ResultAsync } from '@rafikidota/monadia';

const view = await ResultAsync.fromPromise(
  repo.findOneOrFail(id),
  (e) => new NotFoundError(String(e)),
)
  .andThen(loadOrders)
  .map(format)
  .unwrapOr(EMPTY);
Group Members Returns
Construction Ok, Fail (static) ResultAsync
Conversion from, fromPromise (static) ResultAsync
Chainable map, mapOrError, and, andThen, or, orElse ResultAsync
Terminal getValue, getError, mapOr, mapOrElse, expect, unwrap, unwrapOr, unwrapOrElse, unwrapError Promise
Thenable then await ra gives a ResultSync

Callbacks can be sync or async, and andThen accepts a function returning a ResultSync, a ResultAsync or a Promise<ResultSync>, so sync and async steps mix in one chain.

Notes

  • mapOrElse(def, func) takes the error callback first, then the value callback, on both classes.
  • Rejections propagate. A callback that throws inside map/andThen rejects the resulting promise; it is not converted into a Fail. fromPromise is the only entry point from throwing code — its onErr is what keeps E honest. from takes a promise that already resolves to a ResultSync and assumes it does not reject.
  • No *Async methods on ResultSync by design: a method returning Promise<ResultSync<T, E>> is not a ResultSync, so it would not compose.

License

MIT

Keywords