Exception Handling
JavaScript has an Error
class that you can use for exceptions. You throw an error with the throw
keyword. You can catch it with a try
/ catch
block pair e.g.
Error Sub Types
Beyond the built in Error
class there are a few additional built-in error classes that inherit from Error
that the JavaScript runtime can throw:
RangeError
Creates an instance representing an error that occurs when a numeric variable or parameter is outside of its valid range.
ReferenceError
Creates an instance representing an error that occurs when de-referencing an invalid reference. e.g.
SyntaxError
Creates an instance representing a syntax error that occurs while parsing code that isn't valid JavaScript.
TypeError
Creates an instance representing an error that occurs when a variable or parameter is not of a valid type.
URIError
Creates an instance representing an error that occurs when encodeURI()
or decodeURI()
are passed invalid parameters.
Always use Error
Error
Beginner JavaScript developers sometimes just throw raw strings e.g.
Don't do that. The fundamental benefit of Error
objects is that they automatically keep track of where they were built and originated with the stack
property.
Raw strings result in a very painful debugging experience and complicate error analysis from logs.
You don't have to throw
an error
throw
an errorIt is okay to pass an Error
object around. This is conventional in Node.js callback style code which takes callbacks with the first argument as an error object.
Exceptional cases
Exceptions should be exceptional
is a common saying in computer science. There are a few reasons why this is true for JavaScript (and TypeScript) as well.
Unclear where it is thrown
Consider the following piece of code:
The next developer cannot know which function might throw the error. The person reviewing the code cannot know without reading the code for task1 / task2 and other functions they might call etc.
Makes graceful handling hard
You can try to make it graceful with explicit catch around each thing that might throw:
But now if you need to pass stuff from the first task to the second one the code becomes messy: (notice foo
mutation requiring let
+ explicit need for annotating it because it cannot be inferred from the return of runTask1
):
Not well represented in the type system
Consider the function:
Using Error
for such cases is a bad idea as it is not represented in the type definition for the validate function (which is (value:number) => void
). Instead a better way to create a validate method would be:
And now its represented in the type system.
Unless you want to handle the error in a very generic (simple / catch-all etc) way, don't throw an error.
Last updated