2.0.0 • Published 6 years ago

nest-safely v2.0.0

Weekly downloads
2
License
MIT
Repository
github
Last release
6 years ago

:link: Nest Safely

Build Status License Codacy Badge

TL;DR:

Turn this:

const data = { deeply : { nested : { source : { why : 42 } } } };

if (data.deeply && data.deeply.nested && data.deeply.nested.source) {
  console.log(data.deeply.nested.source.why);
}

Into this:

const data = { deeply : { nested : { source : { why : 42 } } } };

console.log(
  Safe(data).deeply.nested.source.why
    [Safe.or]("Nothing found.")
    [Safe.value]
);

The Details

Despite allowing remarkable flexibility and ease of use, when accessing objects in JavaScript it doesn't take long before you run into this sort of error:

const data = { deeply : { nested : { source : 42 } } };
const val = data.deeply.not.here;

// Uncaught TypeError: Cannot read property 'here' of undefined
//  at <anonymous>:2:29

Many libraries exist to solve the problem of accessing deeply nested values in an object, but they all seem to use evaluated strings of access keys like:

const data = { deeply : { nested : { source : 42 } } };
const val = access(data, "deeply.not.here", "nothing");
console.log(val); // nothing

I found this too awkward to use in my code, so I decided to make an improvement using functional programming! I implemented a basic Maybe monad, then used the shiny new ES6 Proxy object to allow for deep object access (and more!) using minimally added syntax. Here's an example:

const Safe = require("nest-safely");

const data = Safe({ deeply : { nested : { source : 42 } } });

const val = data.deeply.no.way.anything.is.here[Safe.or]("nothing was found")[Safe.value];

console.log(val); // "nothing was found"

Notice the use of an additional term [Safe.or] in the chain. You can think of each . in the sequence as a .then in a Promise chain inasmuch as any undefined access will fall through to the end. In this case, a default value is provided to the [Safe.or] method which will not have its functionality invoked if the value has not dropped out in the sequence:

const Safe = require("nest-safely");

const data = Safe({ deeply : { nested : { source : 42 } } });

const val = data.deeply.nested.source.[Safe.or]("nothing was found")[Safe.value];

console.log(val); // 42

The only other piece of machinery added is a [Safe.value] getter keyword that will unwrap the value out of the chain. Optionally, a [Safe.handle] method may also be used where you can supply a function that will be given the last value seen in the chain before null or undefined was encountered.

const Safe = require("nest-safely");

const data = Safe({ deeply : { nested : { source : 42 } } });

const val = data.deeply.nested.does.not.have
  [Safe.handle](last => `Last seen: ${JSON.stringify(last)}`)
  [Safe.value];

console.log(val); // Last seen: { "source" : 42 }

And, just like a Promise chain, [Safe.or] or [Safe.handle] can be located anywhere in the chain:

const Safe = require("nest-safely");

const data = Safe({ deeply : { nested : { source : 42 } } });

const val = data.deeply.nested.does.not.have
  [Safe.or]({ x : { y : 34 } })
  .x
  .nope
  [Safe.handle](last => `Last seen: ${JSON.stringify(last)}`)
  [Safe.value];

console.log(val); // Last seen: { "y" : 42 }

I hope this small 40-line library is useful for you! If you have suggestions, make an issue and I'll be happy to look over it.