cast-error

cast Error for use in TypeScript in catch clausule

Usage no npm install needed!

<script type="module">
  import castError from 'https://cdn.skypack.dev/cast-error';
</script>

README

cast-error

cast Error for use in TypeScript in catch clause

extending npm-version downloads build coverage outdated-deps

language: English also available in: Spanish

Install

$ npm install cast-error

Main goal

The main goal is to have handy way to receive typed Errors y Typescript.

In one hand in Typescript when you use catch(err) the variable err is of type unknown (formerly any). That's why you cannot write err.code for SystemErrors (formerly you can but tsc did not warn you if you make a typo like err.code_num)

In the other hand in Javascript you can throw any variable regardless of its type. You can even throw null. Then it isn't safe to write err.message.

With cast-error this problems are solved in a fancy and efficient way.

Instead of writing this:

try{ somethingCanGoWrong(); }catch(err){ console.error(err.message || err.toString()); throw err; } try{ await fsPromise.unlink('/tmp/aFile.txt'); }catch(err){ if(err.code!='ENOENT') throw err; }

with cast-error you can write:

try{
    somethingCanGoWrong();
}catch(err){
    var error = castError.unexpected(err); // implicit console.err because is unexpected
    throw err;
}

try{
    await fsPromise.unlink('/tmp/aFile.txt');
}catch(err){
    var error = castError.expected(err)
    if(error.code!='ENOENT') throw err;  // code exists in error because is a SystemError
}

Getting deep

Use cases

The main use cases of try/catch are:

  1. To register unexpected error conditions in a way that the programmers can later identify and correct the bug.
  2. To warn the users that there is a problem with their input data.
  3. To handle an exceptional and recoverable situation.

Case 1: logging unexpected errors

It is possible to hook and centralize the way to log error in every catch setting the log function with setLogFunction and then call unexpected in the main cycle and in all points where special behavior is needed.


function initializeSystem(){
    //... other inits...
    var attributes = {code:true,err_num:true,cause:true};
    type MyGlobalError = Error & {[k in keyof typeof attributes]: any}
    castError.setLogFunction(function(context:string, error:MyGlobalError){
        console.log('***********',context);
        var attr: keyof typeof attributes;
        for(attr in attributes){
            console.log(attr,':',error[attr])
        }
    })
}

// In  the main cycle
catch(err){
    throw castError.unexpected(err);
}

// In a function with special needs:
function getStream(name:string){
    try{
        const fd = await fs.open(name)
        var stream = fd.createReadStream()
        this.decorateStream(stream);
        return stream;
    }catch(err){
        var error = unexpected(err)
        console.log('opening stream',name,'in',this.context(error));
        throw error;
    }
}

Case 2: Warning users that there is a problem with their input data.

In some cases, we need to warn users if there are problemas with their input data. For example, if the user wants to delete a file, and the system doesn't find the file it must warn the user.

In Node.js fs.exists is deprecated. In the documentation is clear that the way is to use the file and capture the error to know if the file was not found:

import * as fs from 'fs/promises';
import {expected} from 'cast-error';

function openFileAndProcess(filename:string){
    try{
        var stream = fs.createReadStream(filename)
        var result = process(stream);
        return result;
    }catch(err){
        var error = expected(err);
        if(error.code=='ENOENT'){
            return {
                ok:false,
                message:'file not found'
            }
        }
        throw error;
    }
}

There are many caveats to observe:

  1. If the system is a typical web application is reasonable to think that there is a table with the names of the files that can be delete by the user. If opening (or deleting) a file that is suppose to exists, any error is an unexpected error. And, because of that, is part of the Case 1.
  2. Not all programs are the typical web application. A program can be a command line one or an administration web application. In these cases, the file table may be not exists.
  3. In any case if it is a web application is mandatory to take care of attackers. So in the error messages the system shouldn't send more information that what the user can know.
  4. If there no validations to a whitelist there be other validations: the folder, the type of file (or its extension), and the logical ownership of the file.

Caso 3: To handle an exceptional and recoverable situation.

[... in progress ...]

Type system

License

MIT