@lohmander/maybe
v1.1.1
Published
A small, pragmatic TypeScript Maybe/AsyncMaybe helper focused on real-world JavaScript ergonomics — not a purist FP framework.
Downloads
108
Readme
maybe
A small, pragmatic TypeScript Maybe/AsyncMaybe helper focused on real-world JavaScript ergonomics — not a purist FP framework.
npm install @lohmander/maybeImperative style:
import { Maybe, AsyncMaybe } from "@lohmander/maybe";
async function getPosts(id: string) {
// ... fetch posts
}
async function getComments(id: string) {
// ... fetch comments
}
function getUserActivity(id: string) {
const user = getUser(id);
return AsyncMaybe.fromPromise(user)
.assign({
posts: (u) =>
Maybe.fromNullable(u)
.filter((u) => u.isAuthor)
.flatMap((u) => AsyncMaybe.fromPromise(getPosts(u.id)))
.withDefault([]),
comments: (u) =>
Maybe.fromNullable(u)
.flatMap((u) => AsyncMaybe.fromPromise(getComments(u.id)))
.withDefault([]),
})
.getOrElse({ posts: [], comments: [] });
}Functional style:
import { pipe, piped } from "remeda";
import * as M from "@lohmander/maybe";
async function getPosts(id: string) {
// ... fetch posts
}
async function getComments(id: string) {
// ... fetch comments
}
function getUserActivity(id: string) {
const user = getUser(id);
return pipe(
user,
M.fromPromise,
M.assign({
posts: piped(
M.fromNullable,
M.filter((u) => u.isAuthor),
M.flatMap((u) => M.fromPromise(getPosts(u.id))),
M.withDefault([]),
),
comments: piped(
M.fromNullable,
M.flatMap((u) => M.fromPromise(getComments(u.id))),
M.withDefault([]),
),
}),
M.getOrElse({ posts: [], comments: [] }),
);
}This project aims to be:
- Practical: simple, predictable semantics for everyday code.
- JavaScript-first: behave like JS developers expect (null/undefined are the only "nothing" values; falsy but valid values like
0,'',falseare preserved). - Seamless between sync & async: the async variant (
AsyncMaybe) mirrorsMaybeand flattens sensible return types so you can switch between sync and async flows with minimal friction. - Type-friendly: useful TypeScript overloads and helper types to keep inference ergonomic.
Core ideas
- Maybe holds either a present value or
null|undefined(the canonical Nothing). - AsyncMaybe wraps a
Promise<T | null | undefined>and mirrors Maybe's API. flatMap/map/filterare chainable and short-circuit on Nothing.- Async mappers may return a
Maybe,AsyncMaybe, a raw value, or aPromiseof any of those — the implementation normalizes and unwraps one level to avoid nested containers.
Why this approach
- Many FP libraries carry a lot of academic surface area and strict laws. This library intentionally trades purism for ergonomics:
- Explicit
fromNullableconversion makes null handling deliberate. - No automatic flattening of arbitrary monads — only
MaybeandAsyncMaybeare recognized. - APIs are small and predictable, making them easy to adopt and compose with existing JS code.
- Explicit
When to use it
- To avoid scattering
null/undefinedchecks across code paths. - For composable value pipelines that may involve asynchronous calls.
- When you want a light-weight, readable option type without heavyweight FP constraints.
Notes & recommendations
- Prefer
withDefault/getOrElseat the edges where you need raw values. - Use
filterwith type guards to narrow types safely. assign/extendhelpers are provided for common object-building patterns inside containers.- Add
ofor other Fantasy-Land compatibility shims only if you need them; they are intentionally omitted to keep the surface minimal.
Contributing
- Contributions welcome. Open an issue or PR for bugs, features, or improvements.
- Tests and documentation updates appreciated.
