A tiny, dependency-free Result type for TypeScript. ESM and CommonJS compatible. A value that is either Ok (success) or Bad (tagged failure), so errors live in the type system instead of in thrown exceptions - as usual.
I couldn't find myself satisfied with the existing solutions I met. Sometimes, I found them too sophisticated (complex toolkit, hard to understand when to use which), or too abstracted (left/right formalization). They are great at handling any branches, as they are designed in a formal, abstracted way. They suit all cases. But all felt distant from a result-centered usage. this project suggest another, simpler way to model results. A result is always either a success (Ok) or a tagged failure (Bad) - or even multiple ones.
Let's say you want to handle any case from an external API response. You either receive the expected data from a success, or you handle one of several error cases. A Bad result mirrors this. It takes a first argument, the reason, and an optional second one, a payload carrying curated informations about the failure labelled by that reason. Because one caught error should always be labelled as exactly what it represents. You never uses one error message for 2 distinct error cases. It's as simple as that.
npm install simple-monad@4.0.2
import { ok, bad, type Bad, type Ok } from 'simple-monad';
function parsePort(raw: string): Ok<number> | Bad<'invalid_port', string> {
const n = Number(raw);
if (!Number.isInteger(n) || n < 1 || n > 65535)
return bad('invalid_port', raw);
return ok(n);
}
const r = parsePort('8080');
if (r.isOk())
console.log(r.value); // narrowed: number
else console.error(r.reason); // narrowed: "invalid_port"
A function's failure type can be an union of Bad leaves — so every tag stays paired with its own payload, and handlers receive precisely typed values. When a single if isn't enough, an opt-in Result wrapper adds chaining (map, match) and a static toolkit (unwrap*, matchBad).
The API reference and the guides are generated from the source using typedoc:
You can also find six runnable examples, from plain narrowing to folding multi-tag failures. Each page embeds the actual source of a file in
examples/.
Project setup and commands are covered in CONTRIBUTING.md. All contributions are welcomed, please open a pull request or an issue.