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

tri-fp

v4.0.5

Published

Eliminate try-catch and make it possible to handle all 3 outcomes differently: result, native exception, & application error.

Downloads

353

Readme

tri-fp 🔱

Eliminate try-catch when you code, AND get access to all 3 outcomes of "throwers".

  • 0 dependencies
  • 100% test coverage
  • 400 bytes minified & compressed

Sneak peak

import { tri } from "tri-fp";

const [error, result] = tri(myFuncThatMightThrow)(arg1, arg2);
if (error) // ...
// continue with result

This module aims to:

  • Make both functions and promises more functional by removing the need for try-catch.
  • Handle application errors and native exceptions differently.
  • Not alter native JS constructs, instead only provide tiny wrappers.
  • Increase readability & control.

Wrap regular functions & promises to make it possible to handle all 3 outcomes differently: Results, Native Exceptions, & Application Errors.

                     +--------+
                +--->| Result |
                |    +--------+
+----------+    |
| Function |    |    +------------------+
|    /     |----|--->| Native exception |
| Promise  |    |    +------------------+
+----------+    |
                |    +-------------------+
                +--->| Application error |
                     +-------------------+

What? Why?

In JavaScript, application errors and native exceptions are mixed and can not be easily separated and handled differently. You should handle the first category gracefully and let the user know, and on the contrast make your actual bugs let you know about them quickly by crashing. Read more about this under "Motivation" below.

Get Started

npm i tri-fp

API

tri & triAsync

Wrap a function in tri, or a promise in triAsync, to enable the handling of all 3 outcome categories differently:

  • Native exceptions are still thrown.
  • The return from the new function/promise will be an error-first pair, [error, result].
import { tri, triAsync } from "tri-fp";

// Sync
const [error, result] = tri(myFunction)(arg1, arg2);

// Async, direct promise
const [error, result] = await triAsync(myPromise);

// Async, promise returning function
const [error, result] = await triAsync(myPromiseReturningFunc)(arg1, arg2);

// Native errors/exceptions will still throw.
// Don't catch these; Find your bug.

// Of course check the error in some way after using one of the above:
if (error) // ...

bi & biAsync

Sometimes we actually know that native exceptions can be treated as application errors (e.g. JSON.parse). Thus, a bi/biAsync wrapper is also provided ("bi" for splitting to only 2 outcomes instead of 3/"tri") that convert native exceptions to application errors (Error instance). (See "Advanced use" section below to make your own Error conversion).

import { bi, biAsync } from 'tri-fp';

// NOTE: Will not throw native exceptions, but convert them to Error.
//       Don't use this if you're not sure why.
const [error, result] = bi(myFunction)(arg1, arg2);
const [error, result] = await biAsync(myPromise);
const [error, result] = await biAsync(myPromiseReturningFunc)(arg1, arg2);

One example where bi would be useful is handling of dynamic JSON:

import { bi } from 'tri-fp';

const safeParse = bi(JSON.parse);
const [error, result] = safeParse(data);

if (error) {
  // ...
}

tryCatch & tryCatchAsync

For completeness (& only 2 extra lines of code in this source) a basic try-catch wrapper is also provided: tryCatch/tryCatchAsync that only converts try-catch patterns to error-first pairs. These 2 functions are however not recommended -- you should use one of the above.

import { tryCatch, tryCatchAsync } from 'tri-fp';

// DOUBLE WARNING! This will keep native exceptions as is, but not throw them!
const [error, result] = tryCatch(myFunction)(arg1, arg2);
const [error, result] = await tryCatchAsync(myPromise);
const [error, result] = await tryCatchAsync(myPromiseReturningFunc)(arg1, arg2);

Motivation

Read about the subject of problems with Promises and why one would avoid using throw, try/catch, etc. in some references below.

The motivation for 2 different names, tri & triAsync is to keep it, or make it, clear that a promise is something different where we need to use e.g. await. We can then also support promise returning functions, which is a preferred way to create promises anyway (closer to a more functional "task").

Advanced use

Custom Error Transform

It is possible to access the raw wrappers, and provide your own Error transforming function.

import { tryWrap, tryWrapAsync } from 'tri-fp';

const throwAllBut = (errorTypes) => (err) => {
  if (errorTypes.includes(err.constructor)) return err;
  throw err;
};

const tryFunc = tryWrap(throwAllBut([MyError]));

const [error, result] = tryFunc(myFunction);
// Perhaps error == MyError

Use tryWrapAsync the same way, and it will work like triAsync/biAsync/tryCatchAsync.

Empty Value Configuration

Another additional feature is that it is possible to configure that all empty values instead of undefined should be some other value, i.e. null.

import { setNoneValue } from 'tri-fp';

setNoneValue(null);

// All uses of the tri-fp lib will now return null at empty spaces in the error-first pairs.

Use with Either

If you want to use the even more fp style Either type, here is an example with sanctuary.

import * as S from 'sanctuary';
import { tri as baseTri } from 'tri-fp';

// arrayPairToEither :: (Array2 Error a) -> (Either Error a)
const arrayPairToEither = ([e, r]) => (e ? S.Left(e) : S.Right(r));

// tri :: (*... -> a) -> (*... -> (Either Error a))
const tri = (f) => S.unchecked.pipe([baseTri(f), arrayPairToEither]);
// Now use tri as described above

Recommended eslint

eslint-plugin-functional

{
  "plugins": ["plugin:functional/no-exceptions"]
}

Avoid using try-catch or .catch.

Inspired by / Thanks to

Licence

Hippocratic 2.1