cast-error
v0.1.1
Published
cast Error for use in TypeScript in catch clausule
Downloads
191
Readme
cast-error
cast Error for use in TypeScript in catch clause
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:
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:
- To register unexpected error conditions in a way that the programmers can later identify and correct the bug.
- To warn the users that there is a problem with their input data.
- 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:
- 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.
- 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.
- 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.
- 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