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 🙏

© 2024 – Pkg Stats / Ryan Hefner

eitherwise

v1.2.3

Published

Eitherwise is a TypeScript library for working with functional programming concepts such as Options and Eithers. It provides a set of utilities and functions for handling errors and unexpected cases in a functional and intuitive way. With Eitherwise, you

Downloads

4

Readme

eitherwise

Provides an alternative way of handling errors and unexpected cases with the use of Option and Either

eitherwise is a lightweight library providing utility functions for handling errors and optional values in a functional programming style. It includes three APIs: Option, Either, and EitherAsync. These APIs help you to write cleaner, more expressive, and less error-prone code, particularly when dealing with operations that might fail or return optional values.

If you are looking for more functionality you probably want to look at https://gcanti.github.io/fp-ts/ or https://effect-ts.github.io/effect/

Table of Contents

Installation

To install the package, run the following command:

npm install eitherwise

Examples

A example todo app showing how to use eitherwise to fetch and handle data from an API

https://github.com/Mumma6/eitherwise/tree/main/examples/todo

Quick guide

This quick guide demonstrates how using the eitherwise library allows you to write shorter and more expressive code compared to traditional approaches that use if statements to check for null or undefined values.

Option guide

Instead of checking for null or undefined values with if statements, you can use Option to wrap your value and handle the absence of a value in a more expressive way: Option is a data type that represents the presence or absence of a value. It can be used to handle optional values and prevent runtime errors caused by null or undefined. This library comes with utility functions for working with optional values and safely chaining operations that might return an optional value.

/** Option*/
// Traditional approach
const getValue = () => {
  const value = getValueFromSomewhere()
  if (value !== null && value !== undefined) {
    return value
  } else {
    return "Default value"
  }
}

// With Option
const getValueOption = () => {
  const value = getValueFromSomewhere()
  const option = getOption(value)
  return getOptionOrElse(option, "Default value")
}

Either guide

Instead of checking for errors with if statements and throwing exceptions, you can use Either to wrap the result of your operation and handle errors in a more expressive way:

// Traditional approach
const divide = (a: number, b: number) => {
  if (b === 0) {
    throw new Error("Cannot divide by zero")
  } else {
    return a / b
  }
}

// With Either
const divideEither = (a: number, b: number) => {
  return b === 0 ? left("Cannot divide by zero") : right(a / b)
}

// Get the result
const result = divideEither(10, 2)
const elseValue = getEitherOrElse(result, "Default value")
console.log(elseValue) // 5

EitherAsync guide

Instead of using callbacks or promises and checking for errors with if statements, you can use EitherAsync to handle asynchronous operations and errors in a more expressive way:

// Traditional approach with promises
const doSomethingAsync = () => {
  return new Promise((resolve, reject) => {
    doSomething((error, result) => {
      if (error) {
        reject(error);
      } else {
        resolve(result);
      }
    });
  });
}

// With EitherAsync
const doSomethingAsyncEither = async () => {
  const result = await doSomethingAsync();
  return rightAsync(result);
}

// Handling exceptions with eitherAsyncTryCatch
const asyncOperation = async () => {
  throw new Error("An error occurred");
}

const asyncResultFromTryCatch = await eitherAsyncTryCatch(asyncOperation, (error) => `Error: ${(error as Error).message}`);

// Folding an async Either instance with eitherAsyncFold
const folded = await eitherAsyncFold(
  asyncResultFromTryCatch,
  (error) => {
    console.error(error);
    return error;
  },
  (value) => {
    console.log("Success:", value);
    return value;
  }
)();
console.log(folded); // Error: An error occurred

APIs

Option

Option is a data type that represents the presence or absence of a value. It can be used to handle optional values and prevent runtime errors caused by null or undefined. It comes with utility functions for working with optional values and safely chaining operations that might return an optional value.

Use Option when you have functions or variables that may return a value or nothing (null or undefined). By wrapping the value in an Option, you can safely manipulate the value and chain operations without having to check for null or undefined.

getOption is used to create an Option instance from a nullable value, such as a function that might return null or undefined

getOptionOrElse is used to get the value of an Option instance, with a default value returned if the Option instance is empty.

optionMap is used to apply a transformation to the value inside an Option instance, with the result being another Option instance. This allows you to safely chain operations that might return an optional value.

optionFold is used to fold an Option instance into a value of a different type, based on whether the Option instance is empty or contains a value. This is useful when you want to handle the case where the Option instance is empty separately from the case where it contains a value.

Either

Either is a data type that represents a value of one of two possible types, typically representing the result of an operation that might fail. It is used to handle errors in a functional programming style, providing a way to chain operations that may fail and transform the values inside the Either instances.

Use Either when you have functions that might fail, and you want to handle errors in a functional way. By wrapping the result in an Either, you can chain operations that may fail, perform transformations on the values, and handle errors in a clean and expressive manner.

Either is a data type that represents a value that can be of one of two types: either a left value of type E, or a right value of type A.

left is a function that returns an instance of Either with a left value of type E.

right is a function that returns an instance of Either with a right value of type A.

isLeft is a type guard function that checks if an Either instance is a Left instance.

isRight is a type guard function that checks if an Either instance is a Right instance.

eitherMap is used to apply a transformation to the value inside an Either instance, with the result being another Either instance.

eitherFlatMap is used to apply a transformation to the value inside an Either instance, with the result being another Either instance. The difference is that the transformation function returns another Either instance, which is then returned directly, rather than wrapped inside another Either.

eitherFold is used to fold an Either instance into a value of a different type, based on whether the instance is a Left or Right. This is useful when you want to handle the case where the instance is a Left separately from the case where it is a Right.

getEitherOrElse is used to get the value of a Right instance, with a default value returned if the instance is a Left.

getEither is used to create an Either instance from a nullable value, such as a function that might return null or undefined.

fromOption is used to create an Either instance from an Option instance, where the Left value is provided by a function that is called when the Option instance is empty.

eitherTryCatch is used to handle exceptions in a function that might throw an error, by catching the error and returning a Left instance with the error value.

EitherAsync

EitherAsync extends the Either API for asynchronous operations. It provides utility functions for handling asynchronous operations that might fail and allows you to chain and manipulate asynchronous results in a functional programming style.

Use EitherAsync when you have asynchronous functions that might fail, and you want to handle errors in a functional way. By wrapping the result in an EitherAsync, you can chain asynchronous operations that may fail, perform transformations on the values, and handle errors in a clean and expressive manner.

leftAsync Creates a Promise that resolves to a Left value of an Either instance.

rightAsync Creates a Promise that resolves to a Right value of an Either instance.

eitherAsyncMap Applies a transformation to the value inside a Promise of an Either instance, with the result being another Promise of an Either instance. This allows you to safely chain asynchronous operations that might return an optional value.

eitherAsyncFlatMap Similar to eitherAsyncMap, but the transformation function itself returns a Promise of an Either instance, allowing you to chain asynchronous operations that depend on the previous operation's result.

eitherAsyncFold Folds a Promise of an Either instance into a value of a different type, based on whether the Either instance is Left or Right. This is useful when you want to handle the error case separately from the success case.

eitherAsyncTryCatch Tries to execute an asynchronous function and returns an Either instance. If the function throws an error, it is caught and transformed into an error value of the specified type, wrapped in a Left instance. If the function executes successfully, the result is wrapped in a Right instance. This is useful for handling asynchronous operations that might throw an error.

Usage

Option usage

import { getOption, getOptionOrElse, optionFold, optionMap } from "eitherwise"

// Example of an operation that might return a null value
const getNumber = (): number | null => {
  return Math.random() > 0.5 ? 42 : null
}

// Creating an Option from a nullable value
const numberOption = getOption(getNumber())

const elseValue = getOptionOrElse(numberOption, "Default value")
console.log(elseValue) // 42 (if the value is not null) or "Default value" (if the value is null)

// Mapping operations on Option
const doubledOption = optionMap(numberOption, (x) => x * 2)

// Folding an Option instance
const foldValue = optionFold(
  doubledOption,
  () => "Default value",
  (value) => value
)
console.log(foldValue) // 84 (if the value is not null) or "Default value" (if the value is null)

// Example using optionMap with multiple functions
const double = (x: number): number => x * 2
const addTen = (x: number): number => x + 10
const square = (x: number): number => x * x

// Creating an Option from a nullable value
const numberOption2 = getOption(getNumber())

// Applying multiple functions using optionMap
const transformedOption = optionMap(numberOption2, double, addTen, square)

// Get the result
const transformedElseValue = getOptionOrElse(transformedOption, "Default value")
console.log(transformedElseValue) // 8836 (if the value is not null) or "Default value" (if the value is null)

Either usage

import {
  left,
  right,
  isLeft,
  isRight,
  eitherMap,
  eitherFlatMap,
  eitherFold,
  getEitherOrElse,
  getEither,
  fromOption,
  eitherTryCatch,
  Either,
} from "eitherwise"

// Example of an operation that might fail
const divide = (a: number, b: number) => (b === 0 ? left("Cannot divide by zero") : right(a / b))

// Check if an Either instance is Left or Right
const eitherResult = divide(10, 2)
console.log(isLeft(eitherResult)) // false
console.log(isRight(eitherResult)) // true

// Mapping and chaining operations
const mapped = eitherMap(eitherResult, (x) => x * 2)
console.log(mapped) // {_tag: 'Right', right: 10}

// We can also map other Eithers
const flatten = eitherFlatMap(eitherResult, (x) => right(x * 2))
console.log(flatten) // {_tag: 'Right', right: 10}

// Folding an Either instance
const result = eitherFold(
  eitherResult,
  (error) => console.error("Error:", error),
  (value) => console.log("Success:", value)
)
console.log(result) // Success: 5

// Getting the value or providing a default
const valueOrDefault = getEitherOrElse(eitherResult, () => "Default value")
console.log(valueOrDefault) // // { _tag: "Right", right: "Hello World! }

// Creating an Either from a nullable value
const nullableValue = null
const eitherFromNullable = getEither("Value is null or undefined", nullableValue)
console.log(eitherFromNullable) // // {_tag: 'Left', left: 'Value is null or undefined'}

// Converting an Option to an Either
const optionValue = getOption("Hello, world!")
const eitherFromOption = fromOption(() => "Value is None", optionValue)
conole.log(eitherFromOption) // { _tag: "Right", right: "Hello World! }

const unsafeDivide = (x: number, y: number): number => {
  if (y === 0) {
    throw new Error("Cannot divide by zero")
  }
  return x / y
}

// Handling exceptions with eitherTryCatch
const divideEither = (x: number, y: number): Either<string, number> =>
  eitherTryCatch(
    () => divide(x, y),
    () => "Something went wrong"
  )
console.log(divideEither(4, 2)) // Right(2)
console.log(divideEither(4, 0)) // Left("Something went wrong")
console.log(divideEither("a", 2)) // Left("Something went wrong")

EitherAsync usage

// Example of an async operation that might fail
const asyncDivide = async (a: number, b: number): Promise<Either<string, number>> => {
  return b === 0 ? leftAsync("Cannot divide by zero") : rightAsync(a / b)
}

// Mapping and chaining async operations
const asyncResult = asyncDivide(10, 2)
const asyncMapped = await eitherAsyncMap(asyncResult, (x) => x * 2)
const asyncChained = await eitherAsyncFlatMap(asyncResult, async (x) => rightAsync(x * 2))

// Folding an async Either instance
const folded = await eitherAsyncFold(
  asyncResult,
  (error) => {
    console.error("Error:", error)
    return error
  },
  (value) => {
    console.log("Success:", value)
    return value
  }
)
console.log(folded) // Success: 5

// Handling async exceptions with eitherAsyncTryCatch
const asyncOperation = async () => {
  throw new Error("An error occurred")
}

const asyncResultFromTryCatch = await eitherAsyncTryCatch(asyncOperation, (error) => `Error: ${(error as Error).message}`)

// Now you can fold the asyncResultFromTryCatch to handle the error or success case
const asyncFoldError = await eitherAsyncFold(
  asyncResultFromTryCatch,
  (error) => {
    console.error(error)
    return error
  },
  (value) => {
    console.log("Success:", value)
    return value
  }
)()
console.log(asyncFoldError) // Error: An error occurred

// Successful async operation
const successfulAsyncOperation = async () => {
  return "Operation successful!"
}

const successfulAsyncResultFromTryCatch = await eitherAsyncTryCatch(
  successfulAsyncOperation,
  (error) => `Error: ${(error as Error).message}`
)

// Fold the successfulAsyncResultFromTryCatch to handle the error or success case
const asyncFoldSuccess = await eitherAsyncFold(
  successfulAsyncResultFromTryCatch,
  (error) => {
    console.error(error)
    return error
  },
  (value) => {
    console.log("Success:", value)
    return value
  }
)
console.log(asyncFoled) // Success: Operation successful!