Proper non-string Javascript exceptions - javascript

Somehow this does not feel like the culmination of the 50 years programming language development:
throw "My exception message here";
What's the correct way to do exceptions in Javascript, so that
They can be identified (instanceof)
They can carry other payload besides the default message and stack trace
They "subclass" base Exception, so that debug console and such can extract meaningful information about the exception
Possible nested exceptions (converting exception to another): if you need to catch an exception and rethrow new one the orignal stack trace would be preserved and could be meaningfully read by debugging tools
They follow Javascript best practices

throw new Error("message");
or if you want to be more specific use one of the Error Objects
It's important to make sure you throw real errors because they contain the stack trace. Throwing a string is stupid because it doesn't have any meta data attached to it.
You can also subclass errors
// for some sensible implementation of extend
// https://gist.github.com/1441105#file_1pd.js
var MyError = extend(Object.create(Error.prototype), {
...
});

A base "exception" in JavaScript is built-in Error object:
throw new Error("My exception message here");
You can define your custom exceptions as:
function CustomError(message) {
this.message = message;
}
CustomError.prototype = new Error();
CustomError.prototype.constructor = CustomError;
Check for exception type with instanceof. There is also a handy list of built-in exceptions.

You can't do it, and satisfy the question requirements.
Why?
The problem is getting the stack. Native error object (NativeError from now on), initializes the stack when its constructor is called. To get the stack in MyError (your error subclass), you have to call the NativeConstructor inside MyError's constructor. So, your MyError class looks something like this:
function MyError(msg) {
Error.call(this, msg);
}
But, this does not work. Because according to HTML5 spec:
"if you call Error as a function, it returns a new Error object".
Instead of Error's constructor being nice and initializing your custom error class, it creates an error object that you have no use for. I haven't found a way to call NativeError constructor and have it initialize MyError subclass.
Not all is lost.
If we relax the original question requirements, and forget about "instanceof" to test exception class (so Java anyway), and use "name" instead, it can be done. Just declare MyError as:
function MyError(msg, customArg) {
var e = new Error(msg);
e.name = "MyError";
e.custom = customArg;
return e;
}
This is what I do. Instead of switching on "instanceof", switch on "name" instead.

You can create an Error object by calling the Error constructor.
An error object can have a message and a name. When catching you can check for a specific name or you can create a custom Error Type by inheriting the Error prototype. This allows the use of instanceof to differ between different Error types.
// Create a new object, that prototypally inherits from the Error constructor.
function MyError(message) {
this.message = message || "Default Message";
}
MyError.prototype = new Error();
MyError.prototype.constructor = MyError;
try {
throw new MyError();
} catch (e) {
console.log(e.name); // "MyError"
console.log(e.message); // "Default Message"
}
try {
throw new MyError("custom message");
} catch (e) {
console.log(e.name); // "MyError"
console.log(e.message); // "custom message"
}
Example taken from: https://developer.mozilla.org/en/JavaScript/Reference/Global_Objects/Error

Related

How can I customize exception formatting in jest in order to pretty-print my Error subclasses?

Writing tests in node.js, I would like to hook into jest to allow custom formatting of unhandled exceptions. I want to do that to pretty-print subclasses of Error that I throw in the code under test. Currently I only see the message property of the base Error get printed on test failure, because for reasons outside my control the subclasses are defined like this:
class SomeCustomError extends Error {
constructor(custom, stuff, message) {
super(message);
this._custom = custom;
this._stuff = stuff;
}
}
For example, I imagine defining toString methods on certain exceptions, and then configuring a jest custom error handler, which would call error.toString() and then print the return value (or a custom formatter, which would return the return value):
toString(): {
return `${this._custom} ${this._stuff} ${this._message}`
}
then maybe something like (in jest.config.js):
module.exports = {
...
unhandledErrorFormatterHook: (error) => `A jest test raised an unhandled exception: ${error.toString()}`,
...
}
How can I do that for every test? Rather than decorating every test case using a higher-order function directly in my test code, I'd prefer to use built-in jest features to do this if possible.
This could be achieved with a custom Reporter.
The catch with a custom reporter is the original Error is serialized into a plain object. Any non enumerable properties of the error are removed like the type, stack, message and name so it might involve some duck typing to detect when a SomeCustomError has occurred.
For any tests with a status of failed, a failureDetails array will be populated containing the serialized error object.
Any of the Reporter interface functions that receive a TestResult will be able to access the failureDetails array directly. Otherwise in an AggregatedResult via:
aggregate_result.testResults[].testResults[].failureDetails[]
There is also the failureMessages property of a TestResult that contains a similar array but of the stringified errors, which does include the name, message and stack information.
MyCustomReporter.js
function logTestFailureDetails(test_result){
if (test_result.status !== 'failed') return
console.log('Failed test:', test_result.fullName)
for (const err of test_result.failureDetails) {
// err will have the properties, like `_custom` and `_stuff`.
console.log('failureDetails', err)
}
}
class MyCustomReporter {
// After each test
onTestCaseResult(test, test_result, aggregate_result){
console.log('MyCustomReporter onTestCaseResult:')
logTestFailureDetails(test_result)
}
// Summary at the end
onRunComplete(contexts, results) {
console.log('MyCustomReporter onRunComplete:')
for (const aggregate_result of results.testResults) {
aggregate_result.testResults.forEach(logTestFailureDetails)
}
}
}
module.exports = MyCustomReporter
Jest package.json config
{
"jest": {
"reporters": [
"<rootDir>/MyCustomReporter.js"
]
}
}
It would be worth setting well defined codes on the the custom errors so they can be detected easily. Maybe even the name if you don't mind the error name becoming enumerable elsewhere.
class CustomError extends Error {
constructor(message){
super(message)
this.code = 'CNX'
this.name = this.constructor.name
}
}
It might also be possible to extend the jest DefaultReporter and friends to produce the required output. The failure message appears to be generated in the core code though, so it might take some string manipulation in the reporters to get you there.

Should I use console.error() or throw new Error()

I've seen both:
throw new Error(error);
&
console.error(error);
E.G:
jQuery:
if ( !w.document ) {
throw new Error( "jQuery requires a window with a document" );
}
&
Vue.js:
if (config.warnHandler) {
config.warnHandler.call(null, msg, vm, trace);
} else if (hasConsole && (!config.silent)) {
console.error(("[Vue warn]: " + msg + trace));
}
Both error handling ways seem reliable and used. But my question is:
Is there a difference between them? And if there is, when should I use which?
The key difference: throwing halts the execution, while console.error does not.
Most of the time, it's better to throw an error.
That's a built-in way to signal that something failed and normal execution cannot continue unless the error is expected, caught and handled properly.
In the most platforms, an uncaught exception will be logged to the console as well to warn the developer about it, but caught exceptions won't be logged, since they are assumed to be handled by the code.
Using console.error can be good for cases where the error happened isn't fatal, but you'd like to warn the developer.
However, overusing this feature can easily cause other errors and harder-to-debug code. For example, consider this code:
const elem = document.querySelector('.elem')
if(!elem)
console.error('elem cannot be found!')
const returnValue = functionThatDoesSomethingWithElem(elem)
if(!returnValue.success)
console.error('Doing something with elem has failed!')
if(!returnValue.doSomethingElse())
console.error('Doing something else with elem has failed!')
The above code will log three errors if there's no elem, but execution still continues, maybe causing even more errors.
By throwing an exception, this is avoidable:
const elem = document.querySelector('.elem')
if(!elem)
throw new Error('elem cannot be found!')
const returnValue = functionThatDoesSomethingWithElem(elem)
if(!returnValue.success)
throw new Error('Doing something with elem has failed!')
if(!returnValue.doSomethingElse())
throw new Error('Doing something else with elem has failed!')
This will print only the first error message, and execution halts, unless you put it inside a try..catch structure, like:
try{
const elem = document.querySelector('.elem')
if(!elem)
throw new Error('elem cannot be found!')
const returnValue = functionThatDoesSomethingWithElem(elem)
if(!returnValue.success)
throw new Error('Doing something with elem has failed!')
if(!returnValue.doSomethingElse())
throw new Error('Doing something else with elem has failed!')
}catch(exception){
console.error(exception)
fallbackMethod()
}
There's another difference: thrown errors can be caught by the caller of your function, so it can programmatically handle them (causing the execution to continue, and the error isn't displayed). On the other hand, if you use console.error, the caller can not decide if an error is expected, causing to log the error even if it is normal, so the console can become messy (you can't decide what's a real error and what's not.
Those two are completely different, let's see documentation of each one:
console.error:
Outputs an error message to the Web Console.
Throw:
throws a user-defined exception. Execution of the current function
will stop (the statements after throw won't be executed), and control
will be passed to the first catch block in the call stack. If no catch
block exists among caller functions, the program will terminate.
So, as you can see, they work differently, you can use both in your code, so keep in mind that just a console.error will probably pass unseen by the user, only those with console open will see the message, while throw will be noted, since everything will stop.
Let's see some functional examples:
console.error
Note that the "custom" error will show on console, but code stills executing, you only see the error message because console is showing here in stackOverflow. To stop execution you would need a return right after the console.error
function changeString(currentString, append) {
if (typeof append != 'string') {
console.error("Cannot change string!");
}
//THIS PART BELOW WILL EXECUTE, even with error logged
console.log(currentString + append);
}
changeString("I will show up even with error logged ", null)
Throw
Note that code stops execution when error is thrown and also the message of the error is different from the one that console.error shows.
function changeString(currentString, append){
if (typeof append != 'string'){
throw new Error("Cannot change string!");
}
//THIS PART BELOW WONT EXECUTE
console.log(currentString + append);
}
changeString("I wont show up in the console", null)
Here I added a try/catch, note the catch block being executed, this way I have control over the error and what should happen after the error being thrown, also, I mixed both: Throw and console.error
function changeString(currentString, append) {
try {
if (typeof append != 'string') {
throw new Error("Cannot change string!");
}
console.log(currentString + append);
} catch (er){
console.error("I'm being logged from inside a catch block because an error was thrown");
//DO WHATEVER YOU NEED TO FIX THE ERROR AND ALERT THE USER HERE
}
}
changeString("I wont show up in the console", null)
Further read on official documentations:
Throw
console.error
I would not consider them to be interchangeable. Console.error() simply logs your error to the console with special formatting.
When you actually throw an error you are essentially terminating execution unless you catch the error using a try catch block or some other form of error handling.
Why not have both, like this:
throw new ConsoleError('bad thing');
Source:
// based on https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Error
class ConsoleError extends Error {
constructor(...args) {
const returnValue = super(...args);
console.error(...args);
// Maintains proper stack trace for where our error was thrown (only available on V8)
if (Error.captureStackTrace) {
Error.captureStackTrace(this, ConsoleError);
}
this.name = 'ConsoleError';
this.message = args[0];
// Pass remaining arguments (including vendor specific ones) to parent constructor
return returnValue;
}
}
window.ConsoleError = ConsoleError;
You may also want show the user when some errors happen, for this, use
// this to avoid crashing:
window.toastError('nice user error message', 'extra developer data:', someObject)
// this to crash:
throw new ToastError('we made a mistake')
Source:
window.toastError = (userMsg, ...developerDebugArgs) => {
// TODO: implement toast notifications
// only use userMsg for the toast notification
// Using DOMPurify and rendering userMsg as (potential) html may be nice,
// but backend team may prefer to simple return plain text instead of html
// may need to de-dupe messages being sent to toast component
console.log(
`%c${userMsg}`,
'color: orangered;font-style:italic;',
...developerDebugArgs
);
};
class ToastError extends Error {
constructor(...args) {
const returnValue = super(...args);
window.toastError(...args);
// Maintains proper stack trace for where our error was thrown (only available on V8)
if (Error.captureStackTrace) {
Error.captureStackTrace(this, ToastError);
}
this.name = 'ToastError';
this.message = args[0];
// Pass remaining arguments (including vendor specific ones) to parent constructor
return returnValue;
}
}
window.ToastError = ToastError;
You could also rename the ToastError class ToastException instead, if it suits your fancy

Create an error with native or unknown stack frame location

According to the V8 Stack Trace API, there are errors with native or unknown location. Is it possible to produce these errors with vanilla js?
You can get an error at native with Vanilla JS like so:
> try { String.fromCodePoint(-42); } catch(e) { console.log(e.stack);}
RangeError: Invalid code point -42
at Function.fromCodePoint (native)
at <anonymous>:1:14
In V8, some functions are implemented in JS. For example, String.fromCodePoint was here https://github.com/v8/v8/blob/5.2-lkgr/src/js/string.js#L784. Other functions are implemented in C++. I believe, if the error is thrown in a JS function, it's native. If the function is implemented in C++, the error is not at native. Here's an error from encodeURI, which is implemented in C++ (https://github.com/v8/v8/blob/master/src/uri.cc#L180).
> try { encodeURI('\uDFFF'); } catch(e) { console.log(e.stack);}
URIError: URI malformed
at <anonymous>:1:7
This is a long topic, but v8 is written in c++ and stacks are maintained in c++ and given to javascript when a new error is thrown from c++. but you can send your own customized errors as follows:
1: Capture error stack:
To get the stack of an error you access it by accessing its .stack property.
for example:
fs.readFile('./Index.html', function read(err, data) {
if (err) {
console.log(err.stack);
}
});
2: Sending your own custom stack (any text basically)
Stack property is added on the objects like any property which enables you to do the following:
function trace() {
try {
var err = new Error('myError');
err.stack = "custom written stack!";
throw err;
}
catch(e) {
console.log(e.stack);
}
}
3: Using captureStackTrace to log proper custom stacks
Error.captureStackTrace(targetObject[, constructorOpt])
Creates a .stack property on targetObject, which when accessed returns
a string representing the location in the code at which
Error.captureStackTrace() was called.
function MyError() {
Error.captureStackTrace(this, MyError);
}
// Without passing MyError to captureStackTrace, the MyError
// frame would show up in the .stack property. By passing
// the constructor, we omit that frame and all frames above it.
new MyError().stack
capturing stacks of throw new Errors
To capture the stack of the new thrown error you can write the following:
function trace() {
try {
throw new Error('myError');
}
catch(e) {
alert(e.stack);
}
}
function b() {
trace();
}
calling b will log the following:
Error: myError
at trace (<anonymous>:3:15)
at b (<anonymous>:10:7)
at <anonymous>:1:1

Throwing custom error doesn't preserve Error formatting in the log

JavaScript, when throw-ing a built-in error as such:
throw new Error("Something was wrong");
displays the text nicely - you can't tell you threw an object
However, when creating a custom error by subclassing the Error object (or other error object for that matter), the thrown error is not displayed the same way in the console.
So, by using this code:
var ImproperlyConfigured = function(message){
this.name ="ImproperlyConfigured";
this.message = message || "The object you tried to construct was improperly configured due to an unknown error";
}
ImproperlyConfigured.prototype = new Error();
The following is the output
I don't like the fact that the object properties (name and message) are shown. In fact, I don't like that I don't understand why the properties are shown.
I've googled a bit and I got the feeling that by implementing a toString method will help but, by using this method, the fact that the name of the error is no longer in red puzzles me even more.
Code
var ImproperlyConfigured = function(message){
this.name ="ImproperlyConfigured";
this.message = message || "The object you tried to construct was improperly configured due to an unknown error";
this.toString = function(){
return this.message;
}
}
ImproperlyConfigured.prototype = new Error();
Output:
What I would like to achieve is a standard looking error, by the use of custom error and, of course, by not using the console.error method manually in a try...catch block.
Is this possible?
As Pointy correctly pointed out (pun intended), the issue here is not with JavaScript, but rather with the environment JavaScript is running (in this case, Google Chrome).
In another environment (like Chromium, Firefox, NodeJS, etc.) the behavior will likely be different, using the same code, depending on how those JavaScript hosts are handling these cases.

Instantiate JavaScript Error object instance

I'm slowly working my way to understanding the subtleties of JavaScript. At this time, I'm trying to instantiate an instance of the JavaScript Error object. I know that I can do the following:
var error = new Error('The value is invalid');
error.name = 'ArgumentException';
throw error;
However, I'm trying to figure out/understand if there's a way to condense this instantiation down to a single line. For instance, I'd like to do something like the following:
var error = new Error().{ message:'The value is invalid', name:'ArgumentException'};
throw error;
Is it possible to instantiate an instance of the JavaScript Error object while setting both the message and name properties?
Thank you!
You can assign error.name on the same line as throwing it.
If the right hand side of an assignment is a truthy value, which yours is (a non-empty string), the assignment statement will be truthy, meaning you can run on with &&. The last truthy statement will be sent back to throw at the beginning of the line and will be thrown.
var error = new Error('The value is invalid');
throw (error.name = 'ArgumentException') && error;
Without creating your own Error class with name set to ArgumentException, this is as few statements as you can get to without using an Immediately Invoked Function Expression (IIFE) to have one block statement.
EDIT: adeneo correctly used an IIFE which is one statement, even though it contains multiple statements within it as a block. I've edited my last paragraph to include this.
If you really want everything on a single line, you can do something like
var error = Object.defineProperty(new Error('The value is invalid'), 'name', {value: 'ArgumentException'});
You could also throw it on the same line
throw Object.defineProperty(new Error('The value is invalid'), 'name', {value: 'ArgumentException'});
I'm not really fond of how it looks like, but I guess this answers your question.
I created a custom Error object a while ago following along Mozilla recommendations. Basically it creates an object in an OO fashion (pay attention to prototype and constructor). Caution there are some limitations (type-specific catches; read the resources I mentioned below):
// Create a new object, that prototypilly inherits from the Error constructor.
function MyError(message) {
this.name = "MyError";
this.message = message || "Default Message";
}
MyError.prototype = new Error();
MyError.prototype.constructor = MyError;
try {
throw new MyError();
} catch (e) {
console.log(e.name); // "MyError"
console.log(e.message); // "Default Message"
}
try {
throw new MyError("custom message");
} catch (e) {
console.log(e.name); // "MyError"
console.log(e.message); // "custom message"
}
For further information:
https://developer.mozilla.org/fr/docs/Web/JavaScript/Reference/Global_Objects/Error
The Mozilla page refers to the following question (and caveats you must be aware of):
What's a good way to extend Error in JavaScript?

Categories

Resources