At the moment, when we call a synchronous function that runs some kind of 
operation, there are two obvious ways of returning whether there has been an 
error: returning a boolean or throwing an error.

Returning a boolean to show whether the operation worked is simple, but doesn't 
give any indication as to why an operation may have failed (if it's not 
obvious). For example:

if (!bigComplicatedDataStore.add(someObject)) {
    console.log('There has been an error.')

On the other end of the spectrum, throwing errors allows us to be much more 
verbose about the error. However, there are overheads associated with this, and 
many good practices discourage using throwable errors. Additionally, as 
JavaScript is dynamically typed, the try..catch syntax will inherently catch 
every type of error; not specifically the errors we want to catch.

try {
} catch (error) {
    console.log('Oh no! ' + error.message);

I’m inspired by languages like Rust in how they have tried to reach a middle 
ground between these two methods of error handling, where a detailed object or 
enum is returned that includes either the correct result or information about 
the error. This is a simple way to return error results without the overheads 
of throwing Errors.

Rust has two enums for this purpose: `Option` and `Result` 
( The 
additional benefit of using this method for handling errors in Rust is that, at 
compile time, the Rust compiler will force you to handle the error. While 
JavaScript would not be able to do that, there would still be an advantage to 
having some kind of Result object in the standard library. For example:

function add(data) {
    if (isNotAnObject(data)) {
        return new Result('error', new Error('The data is not an object.'));

    return new Result('success');

const result = add(someObject);
if (result.success) {

if (result.error) {

My design of the `Result` object isn't great, but it's just an example of how 
this could work. This allows us to be verbose in explaining our errors, certain 
about where the error originates from and leaves us without a large overhead.

Encapsulating error handling in an object, like in the examples above, enables 
possibilities of introducing new syntax in the future for handling such errors. 
(This was the case with Promises, which was simplified when the new 
async..await syntax was introduced.)
es-discuss mailing list

Reply via email to