npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

@noctuatech/result

v1.1.0

Published

A Rust-like Result implementation for JavaScript and TypeScript that provides a safe way to handle operations that can succeed or fail.

Readme

Result

A Rust-like Result implementation for JavaScript and TypeScript that provides a safe way to handle operations that can succeed or fail.

Table of Contents

Installation

npm install @noctuatech/result

Overview

The Result library provides a type-safe way to handle operations that can either succeed or fail, inspired by Rust's Result type. Instead of throwing exceptions, functions return a Result that can be either Ok(value) or Err(error).

Key Benefits

  • Type Safety: Compile-time guarantees about error handling
  • No Exceptions: Explicit error handling without try-catch blocks
  • Composable: Chain operations with map, andThen, etc.
  • Async Support: Built-in support for async operations
  • Retry Logic: Built-in retry mechanism with exponential backoff

Core Types

Result<T, E>

A union type representing either success (Ok<T>) or failure (Err<E>).

type Result<T, E> = Ok<T> | Err<E>;

Ok<T>

Represents a successful result containing a value of type T.

Err<E>

Represents a failed result containing an error of type E.

Ok Class

The Ok class represents a successful result.

Properties

ok: true

Always true for Ok instances.

err: false

Always false for Ok instances.

val: T

The contained success value.

Methods

unwrap(): T

Returns the contained Ok value.

const result = new Ok(42);
console.log(result.unwrap()); // 42

unwrapOr<T2>(val: T2): T | T2

Returns the contained Ok value or a provided default. For Ok instances, always returns the contained value.

const result = new Ok(42);
console.log(result.unwrapOr(0)); // 42

expect(msg: string): T

Returns the contained Ok value. For Ok instances, always returns the value without throwing.

const result = new Ok("data");
console.log(result.expect("Should not fail")); // "data"

expectErr(msg: string): never

Throws an error with the provided message. This method is designed to be called on Err instances.

const result = new Ok(42);
result.expectErr("This will throw"); // Throws: "This will throw"

map<U>(mapper: (val: T) => U): Result<U, E>

Maps a Result<T, E> to Result<U, E> by applying a function to the contained Ok value.

const result = new Ok(5);
const doubled = result.map((x) => x * 2);
console.log(doubled.val); // 10

andThen<T2, E2>(mapper: (val: T) => Result<T2, E2>): Result<T2, E | E2>

Calls the mapper function if the result is Ok, otherwise returns the Err value.

const result = new Ok(5);
const chained = result.andThen((x) => new Ok(x * 2));
console.log(chained.unwrap()); // 10

const errorResult = result.andThen(() => new Err("error"));
console.log(errorResult.err); // true

mapErr<F>(mapper: (val: E) => F): Result<T, F>

Maps a Result<T, E> to Result<T, F> by applying a function to a contained Err value. For Ok instances, returns the same Ok value.

const result = new Ok(42) as Result<number, string>;
const mapped = result.mapErr((err) => `Error: ${err}`);
console.log(mapped.unwrap()); // 42 (unchanged)

toString(): string

Returns a string representation of the Ok value.

const result = new Ok("hello");
console.log(result.toString()); // "Ok(hello)"

[Symbol.iterator](): Iterator<T extends Iterable<infer U> ? U : never>

Makes Ok instances iterable when the contained value is iterable.

const result = new Ok([1, 2, 3]);
const values = [...result];
console.log(values); // [1, 2, 3]

Err Class

The Err class represents a failed result.

Properties

ok: false

Always false for Err instances.

err: true

Always true for Err instances.

val: E

The contained error value.

attempted?: boolean

Optional flag indicating if this error has been through retry attempts.

stack: string

The stack trace of the error.

Methods

unwrap(): never

Throws an error with the contained error value and stack trace.

const result = new Err("Something went wrong");
result.unwrap(); // Throws: "Tried to unwrap Error: Something went wrong"

unwrapOr<T2>(val: T2): T2

Returns the provided default value.

const result = new Err("error");
console.log(result.unwrapOr("default")); // "default"

expect(msg: string): never

Throws an error with a custom message and the contained error value.

const result = new Err("network error");
result.expect("Failed to fetch data"); // Throws: "Failed to fetch data - Error: network error"

expectErr(msg: string): E

Returns the contained error value.

const result = new Err("validation failed");
console.log(result.expectErr("Should be an error")); // "validation failed"

map<U>(mapper: (val: T) => U): Err<E>

Returns the same Err instance without applying the mapper.

const result = new Err("error") as Result<number, string>;
const mapped = result.map((x) => x * 2);
console.log(mapped.err); // true
console.log(mapped.val); // "error"

andThen<T2, E2>(mapper: (val: T) => Result<T2, E2>): Err<E>

Returns the same Err instance without calling the mapper.

const result = new Err("error") as Result<number, string>;
const chained = result.andThen((x) => new Ok(x * 2));
console.log(chained.err); // true
console.log(chained.val); // "error"

mapErr<F>(mapper: (val: E) => F): Err<F>

Maps the error value using the provided function.

const result = new Err("original error");
const mapped = result.mapErr((err) => `Mapped: ${err}`);
console.log(mapped.val); // "Mapped: original error"

toString(): string

Returns a string representation of the Err value.

const result = new Err("error message");
console.log(result.toString()); // "Err(error message)"

Result Namespace

The Result namespace provides utility functions for creating and working with Result instances.

Static Methods

Result.ok<T>(val: T): Ok<T>

Creates a new Ok instance with the provided value.

const result = Result.ok(42);
console.log(result.ok); // true
console.log(result.val); // 42

Result.err<T>(val: T): Err<T>

Creates a new Err instance with the provided error value.

const result = Result.err("Something went wrong");
console.log(result.err); // true
console.log(result.val); // "Something went wrong"

Result.wrap<T, E = unknown>(op: () => T): Result<T, E>

Wraps a function that may throw an error into a Result. Catches any thrown errors and returns them as Err.

// Successful operation
const success = Result.wrap(() => JSON.parse('{"valid": true}'));
console.log(success.ok); // true

// Operation that throws
const failure = Result.wrap(() => JSON.parse("invalid json"));
console.log(failure.err); // true
console.log(failure.val instanceof SyntaxError); // true

Result.wrapAsync<T, E = unknown>(op: () => Promise<T>): Promise<Result<T, E>>

Wraps an async function that may throw or reject into a Result. Catches both synchronous throws and promise rejections.

// Successful async operation
const success = await Result.wrapAsync(async () => {
  const response = await fetch("/api/data");
  return response.json();
});

Result.attempt<T, E>(cb: () => Promise<Result<T, E>>, options?: AttemptOptions): Promise<Result<T, E>>

Attempts to execute a callback function multiple times with retry logic.

Options:

  • attempts: number (default: 3) - Number of attempts
  • timeout: number (default: 1000) - Initial timeout in milliseconds
  • backoff: number (default: 0.5) - Backoff multiplier
async function fetchData() {
  return Result.wrapAsync(async () => {
    const response = await fetch("/api/data");

    return response.json();
  });
}

const result = await Result.attempt(fetchData, {
  attempts: 5,
  timeout: 1000,
  backoff: 0.5,
});

Result.isResult<T = any, E = any>(val: unknown): val is Result<T, E>

Type guard that checks if a value is a Result instance.

const result = Result.ok(42);
console.log(Result.isResult(result)); // true
console.log(Result.isResult(42)); // false
console.log(Result.isResult("string")); // false

Examples

Basic Usage

import { Ok, Err, Result } from "@noctuatech/result";

// Creating results
const success = Result.ok(42);
const failure = Result.err("Something went wrong");

// Checking results
if (success.ok) {
  console.log(success.val); // 42
}

if (failure.err) {
  console.error(failure.val); // "Something went wrong"
}

Error Handling

// Safe unwrapping
const result = Result.ok(42);
const value = result.unwrapOr(0); // 42

// With error handling
const riskyResult = Result.wrap(() => JSON.parse(userInput));

if (riskyResult.err) {
  console.error("Invalid JSON:", riskyResult.val);
} else {
  console.log("Parsed data:", riskyResult.val);
}

Chaining Operations

const result = Result.ok(5)
  .map((x) => x * 2) // 10
  .andThen((x) => (x > 10 ? Result.ok(x) : Result.err("Too small")))
  .map((x) => x.toString()); // "10"

if (result.ok) {
  console.log(result.val); // "10"
}

Async Operations

const fetchUser = async (id: number) => {
  return Result.wrapAsync(async () => {
    const response = await fetch(`/api/users/${id}`);
    if (!response.ok) {
      throw new Error(`HTTP ${response.status}`);
    }
    return response.json();
  });
};

const userResult = await fetchUser(123);
if (userResult.ok) {
  console.log("User:", userResult.val);
} else {
  console.error("Failed to fetch user:", userResult.val);
}

Retry Logic

async function fetchWithRetry(url: string) {
  return Result.attempt(async () => {
    return Result.wrapAsync(async () => {
      const response = await fetch(url);

      if (!response.ok) {
        return Result.err(`HTTP ${response.status}`);
      }

      return Result.ok(await response.json());
    });
  });
}

Best Practices

1. Use Result Instead of Throwing

// ❌ Don't throw
function divide(a: number, b: number): number {
  if (b === 0) throw new Error("Division by zero");
  return a / b;
}

// ✅ Return Result
function divide(a: number, b: number): Result<number, string> {
  if (b === 0) return Result.err("Division by zero");
  return Result.ok(a / b);
}

2. Handle Errors Explicitly

// ❌ Don't ignore errors
const result = riskyOperation();
const value = result.unwrap(); // Might throw

// ✅ Handle errors properly
const result = riskyOperation();
if (result.ok) {
  console.log(result.val);
} else {
  console.error("Operation failed:", result.val);
}

3. Use map and andThen for Transformations

// ✅ Chain operations
const result = Result.ok(5)
  .map((x) => x * 2)
  .andThen((x) => (x > 10 ? Result.ok(x) : Result.err("Too small")))
  .map((x) => x.toString());

4. Use wrap for Exception Safety

// ✅ Wrap potentially throwing functions
const result = Result.wrap(() => JSON.parse(jsonString));
if (result.err) {
  // Handle parsing error
}

5. Use Type Guards

// ✅ Check result types
if (Result.isResult(someValue)) {
  // someValue is definitely a Result
  if (someValue.ok) {
    // Handle success
  }
}