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

@crvouga/result

v1.5.0

Published

A comprehensive Result type library for TypeScript with Ok, Err, Loading, and NotAsked states

Readme

result

A comprehensive JavaScript library for handling success and error states with support for remote data patterns. Provides type-safe error handling, pattern matching, and functional programming utilities.

Features

  • Type-safe error handling with Ok and Err types
  • Remote data patterns with Loading and NotAsked states
  • Pattern matching for all result types
  • Functional utilities like map, flatMap, bimap, swap
  • Promise integration with toPromise
  • Nullable value handling with fromNullable and fromUndefined
  • Comprehensive type guards with optional validation
  • Equality comparison and utility functions

Installation

npm install @crvouga/result

Quick Start

import { Ok, Err, Loading, NotAsked, match, mapOk, flatMapOk } from '@crvouga/result';

// Basic usage
const result = Ok(42);
const doubled = mapOk(result, x => x * 2); // Ok(84)

// Error handling
const errorResult = Err('Something went wrong');
const message = match(errorResult, {
  ok: value => `Success: ${value}`,
  err: error => `Error: ${error}`,
  loading: () => 'Loading...',
  notAsked: () => 'Not started'
});

// Remote data patterns
const userData = Loading();
if (isLoading(userData)) {
  console.log('Loading user data...');
}

Core Types

Result Types

  • Ok<T> - Represents a successful result with a value of type T
  • Err<E> - Represents an error result with an error of type E
  • Loading - Represents a loading state (for remote data)
  • NotAsked - Represents an initial state (for remote data)

Type Aliases

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

API Reference

Constructors

Ok(value)

Creates a successful result.

const result = Ok(42);
console.log(result); // { type: "ok", value: 42 }

Err(error)

Creates an error result.

const result = Err('Something went wrong');
console.log(result); // { type: "err", error: "Something went wrong" }

Loading()

Creates a loading state.

const result = Loading();
console.log(result); // { type: "loading" }

NotAsked()

Creates a not-asked state.

const result = NotAsked();
console.log(result); // { type: "not-asked" }

Type Guards

isOk(result, validator?)

Checks if a result is successful, with optional value validation.

const result = Ok(42);
console.log(isOk(result)); // true

// With validation
const isNumber = (value) => typeof value === 'number';
console.log(isOk(result, isNumber)); // true
console.log(isOk(Ok('hello'), isNumber)); // false

isErr(result, validator?)

Checks if a result is an error, with optional error validation.

const result = Err('Network error');
console.log(isErr(result)); // true

// With validation
const isString = (error) => typeof error === 'string';
console.log(isErr(result, isString)); // true
console.log(isErr(Err(404), isString)); // false

isLoading(result)

Checks if a result is in loading state.

const result = Loading();
console.log(isLoading(result)); // true

isNotAsked(result)

Checks if a result is in not-asked state.

const result = NotAsked();
console.log(isNotAsked(result)); // true

isResult(value, valueValidator?, errorValidator?)

Comprehensive type guard for Result types with optional validation.

const result = Ok(42);
console.log(isResult(result)); // true

// With validation
const isNumber = (value) => typeof value === 'number';
const isString = (error) => typeof error === 'string';
console.log(isResult(result, isNumber, isString)); // true

isRemoteResult(value, valueValidator?, errorValidator?)

Type guard for RemoteResult types with optional validation.

const result = Loading();
console.log(isRemoteResult(result)); // true

Pattern Matching

match(result, matchers)

Pattern matching for all RemoteResult states.

const result = Ok(42);
const message = match(result, {
  ok: value => `Success: ${value}`,
  err: error => `Error: ${error}`,
  loading: () => 'Loading...',
  notAsked: () => 'Not started'
});
console.log(message); // "Success: 42"

fold(result, onOk, onErr)

Pattern matching for basic Result types (Ok/Err only).

const result = Ok(42);
const message = fold(result, 
  value => `Success: ${value}`,
  error => `Error: ${error}`
);
console.log(message); // "Success: 42"

foldRemote(result, matchers)

Pattern matching for RemoteResult types with semantic naming.

const result = Ok(42);
const message = foldRemote(result, {
  success: value => `Success: ${value}`,
  failure: error => `Error: ${error}`,
  loading: () => 'Loading...',
  notAsked: () => 'Not started'
});
console.log(message); // "Success: 42"

Transformation Functions

mapOk(result, mapper)

Maps a function over the value of a successful result.

const result = Ok(42);
const doubled = mapOk(result, x => x * 2);
console.log(doubled); // { type: "ok", value: 84 }

mapErr(result, mapper)

Maps a function over the error of an error result.

const result = Err('network error');
const formatted = mapErr(result, error => `Error: ${error.toUpperCase()}`);
console.log(formatted); // { type: "err", error: "Error: NETWORK ERROR" }

flatMapOk(result, mapper)

Maps a function that returns a result over a successful result.

const result = Ok(5);
const processed = flatMapOk(result, x => x > 10 ? Ok(x) : Err('Too small'));
console.log(processed); // { type: "err", error: "Too small" }

flatMapErr(result, mapper)

Maps a function that returns a result over an error result.

const result = Err('network error');
const recovered = flatMapErr(result, error => 
  error.includes('network') ? Ok('Using cached data') : Err(error)
);
console.log(recovered); // { type: "ok", value: "Using cached data" }

bimap(result, okMapper, errMapper)

Maps both success and error cases in one operation.

const result = Ok(42);
const transformed = bimap(result,
  value => value * 2,
  error => `Error: ${error}`
);
console.log(transformed); // { type: "ok", value: 84 }

mapRemote(result, mapper)

Maps over successful RemoteResults, passing through other states unchanged.

const result = Ok({ id: 1, name: 'John' });
const mapped = mapRemote(result, user => user.name);
console.log(mapped); // { type: "ok", value: "John" }

Utility Functions

unwrap(result)

Extracts the value from a successful result, throws on error.

const result = Ok(42);
const value = unwrap(result); // 42

const errorResult = Err('error');
unwrap(errorResult); // throws "error"

unwrapOr(result, defaultValue)

Extracts the value from a successful result, or returns a default.

const result = Ok(42);
const value = unwrapOr(result, 0); // 42

const errorResult = Err('error');
const value = unwrapOr(errorResult, 0); // 0

getOrElse(result, defaultValue)

Alias for unwrapOr for consistency with functional libraries.

const result = Ok(42);
const value = getOrElse(result, 0); // 42

fromNullable(value, error)

Creates a Result from a nullable value.

const user = getUserFromCache();
const result = fromNullable(user, 'User not found in cache');
// Returns Ok(user) if user is not null/undefined, otherwise Err('User not found in cache')

fromUndefined(value, error)

Creates a Result from a value that might be undefined.

const config = process.env.API_KEY;
const result = fromUndefined(config, 'API key not configured');
// Returns Ok(config) if config is not undefined, otherwise Err('API key not configured')

toPromise(result)

Converts a Result to a Promise.

const result = Ok(42);
const promise = toPromise(result);
const value = await promise; // 42

const errorResult = Err('Something went wrong');
const promise = toPromise(errorResult);
try {
  await promise; // throws 'Something went wrong'
} catch (error) {
  console.log(error); // 'Something went wrong'
}

swap(result)

Swaps Ok and Err values of a Result.

const result = Ok(42);
const swapped = swap(result);
console.log(swapped); // { type: "err", error: 42 }

const errorResult = Err('Something went wrong');
const swapped = swap(errorResult);
console.log(swapped); // { type: "ok", value: "Something went wrong" }

equals(a, b, valueEquals?)

Checks if two Results are equal. Safely handles unknown parameters and supports custom equality functions.

const a = Ok(42);
const b = Ok(42);
console.log(equals(a, b)); // true

const c = Err('error');
const d = Err('error');
console.log(equals(c, d)); // true

// Safe with unknown values
console.log(equals(null, Ok(42))); // false
console.log(equals({}, Ok(42))); // false

// With custom equality function for objects
const objA = Ok({ id: 1, name: 'John' });
const objB = Ok({ id: 1, name: 'John' });
console.log(equals(objA, objB)); // false (reference equality)

const deepEquals = (a, b) => JSON.stringify(a) === JSON.stringify(b);
console.log(equals(objA, objB, deepEquals)); // true

// With custom equality for arrays
const arrA = Ok([1, 2, 3]);
const arrB = Ok([1, 2, 3]);
const arrayEquals = (a, b) => a.length === b.length && a.every((x, i) => x === b[i]);
console.log(equals(arrA, arrB, arrayEquals)); // true

fromNullish(value, error)

Creates a Result from a value that might be null or undefined. Returns Ok(value) if the value is not null/undefined, otherwise Err(error).

const result = fromNullish('hello', 'Value is nullish');
// { type: 'ok', value: 'hello' }

const result2 = fromNullish(null, 'Value is nullish');
// { type: 'err', error: 'Value is nullish' }

const result3 = fromNullish(undefined, 'Value is nullish');
// { type: 'err', error: 'Value is nullish' }

fromFalsy(value, error)

Creates a Result from a value that might be falsy. Returns Ok(value) if the value is truthy, otherwise Err(error).

const result = fromFalsy('hello', 'Value is falsy');
// { type: 'ok', value: 'hello' }

const result2 = fromFalsy('', 'Value is falsy');
// { type: 'err', error: 'Value is falsy' }

const result3 = fromFalsy(0, 'Value is falsy');
// { type: 'err', error: 'Value is falsy' }

const result4 = fromFalsy(false, 'Value is falsy');
// { type: 'err', error: 'Value is falsy' }

const result5 = fromFalsy(null, 'Value is falsy');
// { type: 'err', error: 'Value is falsy' }

const result6 = fromFalsy(undefined, 'Value is falsy');
// { type: 'err', error: 'Value is falsy' }

Error Handling

tryCatchSync(fn)

Wraps a synchronous function that might throw.

const result = tryCatchSync(() => {
  const value = JSON.parse('invalid json');
  return value;
});
console.log(result); // { type: "err", error: SyntaxError }

tryCatch(fn)

Wraps an asynchronous function that might throw.

const result = await tryCatch(async () => {
  const response = await fetch('/api/data');
  return response.json();
});

RemoteResult Type Guards

isRemoteSuccess(result, validator?)

Checks if a value is a successful RemoteResult (Ok only).

const result = Ok(42);
console.log(isRemoteSuccess(result)); // true

const loadingResult = Loading();
console.log(isRemoteSuccess(loadingResult)); // false

isRemoteFailure(result, validator?)

Checks if a value is a failed RemoteResult (Err only).

const result = Err('error');
console.log(isRemoteFailure(result)); // true

const successResult = Ok(42);
console.log(isRemoteFailure(successResult)); // false

Examples

Basic Error Handling

import { Ok, Err, isOk, isErr, unwrapOr } from '@crvouga/result';

function divide(a, b) {
  if (b === 0) {
    return Err('Division by zero');
  }
  return Ok(a / b);
}

const result = divide(10, 2);
if (isOk(result)) {
  console.log(`Result: ${result.value}`); // Result: 5
} else {
  console.log(`Error: ${result.error}`);
}

// Or use unwrapOr for default values
const value = unwrapOr(result, 0);

API Response Handling

import { Ok, Err, Loading, match, fromNullable } from '@crvouga/result';

async function fetchUser(id) {
  try {
    const response = await fetch(`/api/users/${id}`);
    if (!response.ok) {
      return Err(`HTTP ${response.status}: ${response.statusText}`);
    }
    const data = await response.json();
    return fromNullable(data.user, 'User not found');
  } catch (error) {
    return Err(`Network error: ${error.message}`);
  }
}

// In your component
const userData = Loading(); // Initial state

const renderUser = (data) => {
  return match(data, {
    ok: user => <UserCard user={user} />,
    err: error => <ErrorMessage error={error} />,
    loading: () => <Spinner />,
    notAsked: () => <button onClick={fetchUser}>Load User</button>
  });
};

Form Validation

import { Ok, Err, fromNullable, flatMapOk } from '@crvouga/result';

function validateEmail(email) {
  return fromNullable(email, 'Email is required')
    |> (r) => flatMapOk(r, email => 
      email.includes('@') ? Ok(email) : Err('Invalid email format')
    );
}

function validateAge(age) {
  return fromNullable(age, 'Age is required')
    |> (r) => flatMapOk(r, age => 
      age >= 18 ? Ok(age) : Err('Must be 18 or older')
    );
}

const emailResult = validateEmail('[email protected]');
const ageResult = validateAge(25);

if (isOk(emailResult) && isOk(ageResult)) {
  console.log('Form is valid');
} else {
  console.log('Validation errors:', {
    email: emailResult.type === 'err' ? emailResult.error : null,
    age: ageResult.type === 'err' ? ageResult.error : null
  });
}

Promise Integration

import { Ok, Err, toPromise, tryCatch } from '@crvouga/result';

// Convert Results to Promises
const result = Ok(42);
const promise = toPromise(result);
const value = await promise; // 42

// Wrap async operations
const fetchData = async () => {
  const result = await tryCatch(async () => {
    const response = await fetch('/api/data');
    if (!response.ok) {
      throw new Error(`HTTP ${response.status}`);
    }
    return response.json();
  });
  
  return result;
};

const data = await fetchData();
if (isOk(data)) {
  console.log('Data:', data.value);
} else {
  console.error('Error:', data.error);
}

Complex Business Logic

import { Ok, Err, match, bimap, swap } from '@crvouga/result';

function processOrder(order) {
  // Validate order
  if (!order.items || order.items.length === 0) {
    return Err('Order must contain at least one item');
  }
  
  if (order.total <= 0) {
    return Err('Order total must be positive');
  }
  
  // Apply business rules
  const processedOrder = {
    ...order,
    status: order.total > 100 ? 'premium' : 'standard',
    discount: order.total > 200 ? 0.1 : 0
  };
  
  return Ok(processedOrder);
}

const order = { items: ['laptop'], total: 150 };
const result = processOrder(order);

// Transform the result for display
const displayResult = bimap(result,
  order => `Order processed: ${order.status} (${order.discount * 100}% discount)`,
  error => `Order failed: ${error}`
);

// Or use pattern matching for complex logic
const message = match(result, {
  ok: order => {
    if (order.status === 'premium') {
      return `🎉 Premium order confirmed! Total: $${order.total}`;
    }
    return `✅ Order confirmed! Total: $${order.total}`;
  },
  err: error => {
    if (error.includes('items')) {
      return `🛒 ${error}. Please add items to your cart.`;
    }
    return `❌ ${error}`;
  },
  loading: () => '⏳ Processing your order...',
  notAsked: () => '🛒 Add items to cart to continue'
});

Contributing

See CONTRIBUTING.md for details on how to contribute to this project.

License

MIT License - see LICENSE for details.

Changelog

See CHANGELOG.md for a list of changes and version history.