4

Let's say I am writing a simple library in JavaScript, with only a few functions:

  • Calculate the factorial of a positive natural number
  • Find the least common multiple from an array of integers
  • And so on...

In each of these functions, I will check if the given parameter is acceptable before performing any operations, and would like to raise an error if it is not. (Raising errors is a good programming practice, right?)

To do this, I can think of a few different options. Take the factorial(x) function, for example:

  • Option 1: simply throw new Error("Invalid argument for factorial(): natural number expected.")

  • Option 2: define a class NotANaturalNumberError extends Error {} in the global scope (!) and then throw new NotANaturalNumberError("Invalid argument for factorial(): natural number expected.")

  • Option 3: almost the same as option 2 but not cluttering the global scope, and instead defining class NotANaturalNumberError extends Error {} in a local scope (such as inside a closure)

They all have pros and cons, though:

  • Option 1:

    • Cons: tricky code would be necessary to tell it apart from other possible errors, since it is just an Error, the superclass.
    • Pros: one simple line of code, does not clutter the global scope
  • Option 2:

    • Cons: clutters the global scope, which is generally considered terrible, and might conflict with something else. Also, the user must somehow be informed that it's called NotANaturalNumberError.
    • Pros: the user of the library can do the check e instanceof NotANaturalNumberError when catching an error
  • Option 3:

    • Cons: the user of the library can't do the check e instanceof NotANaturalNumberError when catching an error since NotANaturalNumberError is not defined externally.
    • Pros: does not clutter the global scope

None of these options seem good enough, and I couldn't think of any other. What is the proper way to do this? How the big libraries out there do this?

  • Option 4 is to return your language's equivalent of an Optional, which i would prefer if it's difficult to discern at compile time whether an operation is valid. – Phoenix Aug 30 '17 at 3:33
4

If you weren't writing a library, I would tell you that distinguishing errors is only useful if you actually need to differentiate the errors, which is not likely and therefore you shouldn't worry about it.

However seeing how you are writing a library, it's always better to err on the side of being too formal, so yes, you should probably be throwing an error relative to the type of error in question.

Of the three options, I'd say option 3 is probably your best solution. The user can simply grab the name from the error thrown if more information is required. You could add properties holding the inputs for added information to the caller.

Though if I may suggestion an option 4, which is to say to give preference towards using existing error types: EvalError, RangeError, ReferenceError, SyntaxError, TypeError, and URIError. You're only ever going to need RangeError and possibly TypeError in all likelihood.

In this way your overall library size is reduced (always a good thing if one of the high points of your library is simplicity), and you can still distinguish errors in your code.

  • Totally agree with @Neil on reusing error types. If you did go with Option 3 you could namespace your custom error types so they are not hidden within a closure ... if you really want the instanceof sugar. – click2install Aug 30 '17 at 15:26

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.