@zemnmez/result
v1.0.4
Published
A tiny, purely functional TypeScript implementation of Rust's Result type
Maintainers
Readme
@zemnmez/result
@zemnmez/result is a TypeScript implementation of Rust's
Result<T, E> type. A Result represents, exactly and in a
type-safe manner, the result of a sequence of any number of operations that
may potentially fail.
The benefit is that every possible failure remains explicit in the return
type, even across long chains of operations. The tradeoff is that a Result
must be handled explicitly before its successful value can be used.
In React, this can effectively eliminate early returns from components that
would otherwise violate the Rules of Hooks. A function that
may fail can return a Result, while the component calls every hook
unconditionally and uses unwrap_or_else to select its success or failure UI:
import {
and_then,
Err,
Ok,
type Result,
unwrap_or_else,
} from '@zemnmez/result';
type Vector3 = readonly [x: number, y: number, z: number];
export function normalize([x, y, z]: Vector3): Result<Vector3, Error> {
const length = Math.hypot(x, y, z);
if (length === 0) {
return Err(new Error('Cannot normalize a zero-length vector.'));
}
return Ok([x / length, y / length, z / length]);
}
declare function useTheme(): { error: string; vector: string };
export function UnitVector({ vector }: { vector: Vector3 }) {
const theme = useTheme();
return unwrap_or_else(
and_then(normalize(vector), vector => (
<output className={theme.vector}>
{vector.map(value => value.toFixed(2)).join(', ')}
</output>
)),
error => <p className={theme.error}>{error.message}</p>
);
}Implementation
Result is implemented purely functionally. Advanced JavaScript and
TypeScript compilers can therefore erase or inline its inner functionality.
With a minifier, no class names, constructor names, property names,
discriminant strings, or symbols identifying Ok or Err need to occupy
space in the resulting bundle.
The representation is intentionally opaque. Inspect a result with is_ok,
is_err, unwrap, unwrap_err, or the provided combinators rather than
runtime properties.
Usage
and_then maps the successful value. and_then_flatten chains an operation
that can itself fail. Curried map_result and bind_result work well in
pipelines:
import { bind_result, Err, Ok, unwrap } from '@zemnmez/result';
export const doubled = bind_result((value: number) =>
value >= 0 ? Ok(value * 2) : Err('negative')
);
export const result = doubled(Ok(21));
export const answer = unwrap(result); // 42pipe_result chains several Result-returning functions from left to right and
stops at the first Err.
