Creating your own errors

# Jonas Sicking (11 years ago)

What is the current expected way for an author to throw a custom errors? You obviously could do something like:

throw { reason: "TimeoutError" };

or

throw "TimeoutError";

However that makes it very hard for anyone receiving an exception to inspect what it is. I.e. you'd first have to check what type of value it is, i.e. if it's a string, something that's instanceof Error, or something with just custom properties.

Instead you can do

throw new Error;

However, as I understand it, that doesn't let you pass machine-readable information in the error. You can only pass a message which is intended for human consumption. I.e. something like:

throw new Error("operation took too long and timed out.");

Yet another alternative is to do

let e = new Error;
e.name = "TimeoutError";
throw e;

This is a little bit more code than expected though.

A shorter alternative that is somewhat compatible with Error is

throw { name: "TimeoutError" };

However this means that instanceof Error returns false, which could mean that code is forced to check which properties are set.

The reason I'm asking is that the DOM has invented a completely new interface, DOMError. This seems pretty heavy-handed and yet another instance of the DOM doing it in its own way rather than using existing ES functionality.

I'd like for the DOM to mimic what we expect authors to do. It's just not obvious to me what authors are expected to do if they want to throw a machine readable error. I.e. one that allows code to catch the error and handle it.

# François REMY (11 years ago)

In some browsers, using the native Error class enables cool features like automatic stack trace.

Just in case you need special kind of Error (ie: subclasses), I recommend doing it that way:

function SomeError(message) {

    // the real instance should be an Error:
    var self = new Error(message);

    // but the prototype could be updated
    if(self.__proto__) { self.__proto__ = SomeError.protoype }
    else { copyProperties(SomeError.prototype, self); }

    // do some more custom things
    // ...

    // return the instance
    return self;

}

SomeError.prototype = Object.create(Error.prototype);

throw new SomeError('abc');
# Domenic Denicola (11 years ago)

Woah, François, that seems pretty overcomplicated; why not replace everything inside the constructor with this.message = message? It has the same effect in the browsers I've seen. (Also don't forget SomeError.prototype.constructor = SomeError.)

Anyway, to Jonas's point: I think DOMError is actually pretty OK. One thing I noticed is that the ES5 spec (and presumably ES6, although I haven't checked) is careful to never throw simple Error objects, but instead TypeErrors or RangeErrors or so on. I infer that they are leaving bare Error for user cases. I think "the platform" should generally follow this lead; that is, the DOM should leave bare Errors to the user, and throw DOMErrors or TypeErrors or RangeErrors for its own errors.

To answer your more general question, authors are strongly discouraged from ever throwing anything that is not instanceof Error, see e.g. Guillermo Rauch's A String is not an Error. Also, the name property is generally the same across all instances of a particular error type, i.e.

errInstance.name === errInstance.constructor.name === errInstance.constructor.prototype.name === Object.getPrototypeOf(errInstance).name

(You can verify this for all the built-in ES5 errors.) Usually a code property is used for more specific information, from what I've seen. But yes, that has to be added manually with a bit of awkwardness, i.e.

const e = new DOMError("The message");
e.code = "TimeoutError";
throw e;
# Chad Austin (11 years ago)

As an example from the field, here is how we extend our own error types: imvu/imvujs/blob/master/src/error.js

You can either switch on e.name or use e instanceof ErrorType in your catch handler.

Like Francois's approach, it also preserves the stack property if the browser supports it.

# Jonas Sicking (11 years ago)

Note that

(new DOMError) instanceof Error;

returns false. So the DOM does do the "strongly discouraged" thing. Is this ok or bad?

Also, the DOM does not create a new class for each different value of .name.

I.e. you can get a DOMError whose .name is "NetworkError" or "AbortError". DOMError even has a constructor which allows setting .name to anything: new DOMError(name, message);

In fact, there are no defined situations where the DOM creates DOMError objects whose .name is "DOMError".

Again, is this ok or is it bad practice?

# Erik Arvidsson (11 years ago)

Jonas, DOM never throws DOMError. DOMErrors are used for other error mechanisms, such as callbacks.

The DOM spec does however throw DOMException objects. These are instanceof Error (see WebIDL)

var ex;
try {
  document.appendChild(document);
} catch (e) {
  ex = e;
}
assert(ex instanceof Error);
assert(ex.contructor === DOMException);
assert(ex.name === 'HierarchyRequestError');

In Blink DOMException also have a stack property

assert(stack in ex);
# Erik Arvidsson (11 years ago)

On Wed, Jul 10, 2013 at 3:49 PM, Erik Arvidsson <erik.arvidsson at gmail.com> wrote:

In Blink DOMException also have a stack property

assert(stack in ex);

Since they are just Error objects

# Domenic Denicola (11 years ago)

From: Jonas Sicking [jonas at sicking.cc]

Note that

(new DOMError) instanceof Error;

returns false. So the DOM does do the "strongly discouraged" thing. Is this ok or bad?

This is horrible! I had no idea! It should definitely derive from Error.

Also, the DOM does not create a new class for each different value of .name.

I.e. you can get a DOMError whose .name is "NetworkError" or "AbortError".

In fact, there are no defined situations where the DOM creates DOMError objects whose .name is "DOMError".

Again, is this ok or is it bad practice?

This seems bad, although in practice not nearly as bad as the broken inheritance. (I guess because getting name correct is subtle; many user-space custom errors do not do so, so you can't always rely on it.) In an ideal world, I think you could do a few things:

  • A hierarchy, where e.g. NetworkError derives from DOMError derives from Error, and all have the correct name properties.
  • A reform, where DOMError derives from Error and both have the correct name properties; but, specific DOMError instances thrown by certain APIs can have a code property containing strings like "NetworkError".

DOMError even has a constructor which allows setting .name to anything: new DOMError(name, message);

This seems quite bad: error constructors always (in ES)/almost always (in user space) take message as their first parameter, with other parameters beyond that being introduced by various user-space errors.

# Allen Wirfs-Brock (11 years ago)

On Jul 10, 2013, at 12:55 PM, Domenic Denicola wrote:

From: Jonas Sicking [jonas at sicking.cc]

DOMError even has a constructor which allows setting .name to anything: new DOMError(name, message);

This seems quite bad: error constructors always (in ES)/almost always (in user space) take message as their first parameter, with other parameters beyond that being introduced by various user-space errors.

IE has a legacy feature that is similar but different which also necessitated a change in the Error constructor:

2.1.142 [ECMA-262] Section 15.11.2.1, new Error (messageOrNumber)
V0214:
When the Error constructor is called with one argument the following steps are taken:
1.	The [[Prototype]] property of the newly constructed object is set to the original Error prototype object, the one that is the initial value of Error.prototype ([ECMA-262] section 15.11.3.1).
2.	The [[Class]] property of the newly constructed Error object is set to "Error".
3.	Let message be the empty string.
 4.	Let number be NaN.
 5.	If messageOrNumber is undefined, then go to step 8.
 6.	Let number be ToNumber(messageOrNumber).
 7.	If number is not NaN, then go to step 9.
 8.	Let message be ToString(messageOrNumber).
 9.	The description property of the newly constructed object is set to message.
 10. If the argument message is not undefined, the The message property of
the newly constructed object is set to ToString(message).
 11. The name property of the newly constructed object is set to "Error".
 12. If number is NaN, then go to step 14.
 13. The number property of the newly constructed object is set to number.
 14. Return the newly constructed object.

I really don't think you want to go down that path...

# Andrea Giammarchi (11 years ago)

I am not sure where this conversation ended up so I hope I am not repeating already said stuff but you can always create constructors that inherits from Error and set the name.

function CustomError(message) {
  this.message = message || '';
}
CustomError.prototype = new Error;
CustomError.prototype.name = CustomError.name;
CustomError.prototype.constructor = CustomError;

// whenever you need
throw new CustomError;

That will be highlighted in most consoles as red CustomError instance you can inspect to read the message.

Hope this helped anyhow.

Best

# François REMY (11 years ago)
function CustomError(message) { 
   this.message = message || ''; 
} 
CustomError.prototype = new Error; 
 
// whenever you need 
throw new CustomError; 

At best, this will not preserve the stack trace property, at worse this will lead to a bad one.

# Claus Reinke (11 years ago)

At best, this will not preserve the stack trace property, at worse this will lead to a bad one.

Because the location info will be that of the new Error? One could try this instead, which seems to work for me:

throw { __proto__ : new Error(), reason: "Monday" }
# François REMY (11 years ago)

Because the location info will be that of the new Error? One could try this instead, which seems to work for me:

throw { __proto__ : new Error(), reason: "Monday" }

That one seems to be working (except in IE<11 since IE didn't support __proto__ prior to that). That somehow surprises me that browser "unwrap" the prototype chain but they seem to do in this case.

# Andrea Giammarchi (11 years ago)

yep, I thought it was about having meaningful red errors with a proper name in console. The stack can be addressed passing the new CustomError(new Error('message')) using error as argument, not big deal ... abusing __proto__ for this seems like the wrong answer to a concrete problem

# Forbes Lindesay (11 years ago)

What's wrong with:


throw new CustomError('message')

function CustomError(message /* accept any custom arguments here */) {
  Error.call(this, message)
  this.name = 'CustomError'
  /**
   * add any custom error info here
   */
}
CustomError.prototype = Object.create(Error)
CustomError.prototype.constructor = CustomError

In ES6 land this becomes:


throw new CustomError('message')

class CustomError extends Error {
  constructor (message /* accept any custom arguments here */) {
    super(message) 
    this.name = 'CustomError'

    /**
     * add any custom error info here
     */
  }
}

Doesn't that seem like an obvious enough solution?

# Andrea Giammarchi (11 years ago)

wrong at least two things for the non ES6 version

  1. Error.call(this, message) does not do what you think it should do
  2. CustomError.prototype = Object.create(Error) has a typo, you meant Object.create(Error.prototype)

In the ES6 version, I am not sure about constructors and classes, but name should be inherited or the console won't even look for it as own property so it should be

class CustomError extends Error {
  name = 'CustomError'
  constructor (message /* accept any custom arguments here */) {
    super(message)
  }
}

only in case CustomError won't have already a name property as it is for ES3<=ES5 constructors.

:-)

# Allen Wirfs-Brock (11 years ago)

You need to say:

class CustomError extends Error {
  get name() {return 'CustomError'}
  constructor (message /* accept any custom arguments here */) {
    super(message)
  }
}

or alternatively:

class CustomError extends Error {
  constructor (message /* accept any custom arguments here */) {
    super(message)
  }
}
CustomError.prototype.name = 'CustomError';

This is one of the rare places where it is inconvenient that class declarations don't provide syntax for defining data properties on the prototype.

Forbes class version should be fine if you don't mind each CustomError instance having an own 'name' data property.

# Jonas Sicking (11 years ago)

Erik Arvidsson wrote:

DOMErrors are used for other error mechanisms, such as callbacks. [1]

Indeed. But this is one of the things I'm trying to fix. It seems very silly that the DOM defines two classes of error objects. Neither of which are following the patterns used by ES.

# Mark S. Miller (11 years ago)

The perpetual hazard with trying to clean things up is, of course, legacy compat constraints. Regarding ES itself, many of those on this list (including myself) feel oriented about when and where these bite and how much, and have successfully navigated through these to many successful cleanups. We have also succeeded at not wasting too much time on attractive cleanups whose adoption was doomed because of these constraints.

Regarding DOM, I am certainly less oriented and I suspect at least some others on this list are too. In the absence of knowledge, it is tempting to imagine the worst and despair of cleaning up anything that is already deployed. Could you give a summary -- necessarily subjective which is fine -- of how you think about what can and cannot be successfully cleanup up here? Thanks.

# Andrea Giammarchi (11 years ago)

oh well ... I've missed the part when you guys decided that inherited defaults are useless as properties ... :-/

as you said, inconvenient, and not only this case ... also good to know ^_^

# Jonas Sicking (11 years ago)

I think we have the following constraints for exceptions thrown by the DOM:

x instanceof DOMException

This probably needs to evaluate to true.

x.code == DOMException.HIERARCHY_REQUEST_ERR
x.code == 3

This definitely needs to evaluate to true for a bunch of older APIs. This used to be the only way to test what type of exception you had. For newer exception types we haven't added a code and so .code returns 0. For those APIs you use .name instead, see below. Obviously the exact constant and number varies with the exception type.

x.name == "HierarchyRequestError"

This should test true. This should be uncontroversial enough that I don't think it matters how much dependency there is, right? Obviously the exact value varies with the exception type.

x.message

This probably needs to contain a human readable error message. This should be uncontroversial enough that I don't think it matters how much dependency there is, right?

Object.toString(x)

I don't think it's important what this returns. I.e. I would guess that we can safely subclass DOMException as needed.

I think we have the following constraints for properties that contain error information in APIs that currently use DOMError (IDBTransaction.error in the IndexedDB spec for example).

x instanceof DOMError

Probably doesn't matter what this returns.

x.name = "AbortError"

This should be uncontroversial enough that I don't think it matters how much dependency there is, right?

x.message

This probably needs to contain a human readable error message. This should be uncontroversial enough that I don't think it matters how much dependency there is, right?

Object.toString(x)

Probably doesn't matter what this returns.

I doubt that in either case it matters if these properties live on the objects themselves, or on the prototype chain.

So all in all, I think we have a lot of room for what to do in both cases. The only requirements is that exceptions likely need to test true for instanceof DOMException, and that they in some cases need to have a specific numeric code property.

My recommendation would be to

  • Get rid of DOMError and use DOMException in its place
  • Add new subclasses for each exception type. I.e. exceptions with .name == HierarchyRequestError should also test true for instanceof HierarchyRequestError
  • Keep specifying that DOMException is a subclass of Error. This is already the case, but I wanted to make it clear that this shouldn't change.

I personally think that the second bullet is a bit silly. But since that appears to be the convention that ES uses then we should stick with it.

# Erik Arvidsson (11 years ago)

On Tue, Jul 16, 2013 at 4:30 PM, Jonas Sicking <jonas at sicking.cc> wrote:

Object.toString(x)

I assume you mean Object.prototype.toString.call(x)

x instanceof DOMError

Probably doesn't matter what this returns.

FWIW, Neither Blink nor WebKit exposes DOMError so this should be safe to change (on the open web at least).

I personally think that the second bullet is a bit silly. But since that appears to be the convention that ES uses then we should stick with it.

Agreed.

There will be issues here since DOM has throw a SyntaxError, which means throw a DOMException with the name SyntaxError and the code DOMException.SYNTAX_ERR so that would mean that we have a naming conflict since ES already have a SyntaxError constructor.

# Jonas Sicking (11 years ago)

On Tue, Jul 16, 2013 at 5:05 PM, Erik Arvidsson <erik.arvidsson at gmail.com> wrote:

There will be issues here since DOM has throw a SyntaxError, which means throw a DOMException with the name SyntaxError and the code DOMException.SYNTAX_ERR so that would mean that we have a naming conflict since ES already have a SyntaxError constructor.

Good point. It's entirely plausible that we could simply use the ES SyntaxError in all cases where DOMException SyntaxError is being thrown today. Would need to be tested though.

# Anne van Kesteren (11 years ago)

Just to relay here what I discussed with Allen in the off-periods of the last TC39 meeting.

On Wed, Jul 17, 2013 at 12:30 AM, Jonas Sicking <jonas at sicking.cc> wrote:

x.name == "HierarchyRequestError" This should test true. This should be uncontroversial enough that I don't think it matters how much dependency there is, right? Obviously the exact value varies with the exception type.

Given that for every JavaScript exception x.name is the name of the object, violating that is actually breaking a pattern. x.name should be DOMException. We could have x.subname that would be something more specific.

I think we have the following constraints for properties that contain error information in APIs that currently use DOMError (IDBTransaction.error in the IndexedDB spec for example).

x.name = "AbortError" This should be uncontroversial enough that I don't think it matters how much dependency there is, right?

This has the same problem. It might be worse here.

My recommendation would be to

  • Get rid of DOMError and use DOMException in its place

Agreed.

  • Add new subclasses for each exception type. I.e. exceptions with .name == HierarchyRequestError should also test true for "instanceof HierarchyRequestError"

Allen and I both agreed this would be overkill. Using .subname or some such should be sufficient.

  • Keep specifying that DOMException is a subclass of Error. This is already the case, but I wanted to make it clear that this shouldn't change.

Agreed.

# Anne van Kesteren (11 years ago)

On Wed, Jul 17, 2013 at 7:10 PM, Jonas Sicking <jonas at sicking.cc> wrote:

Good point. It's entirely plausible that we could simply use the ES SyntaxError in all cases where DOMException SyntaxError is being thrown today. Would need to be tested though.

SyntaxError is used by JavaScript for syntax errors in JavaScript. We shouldn't use it for where we throw "SyntaxError" in platform APIs today per discussion with Allen. (He considered the usage for JSON already as a mistake.)

# Brendan Eich (11 years ago)

Problem is, "TypeError" for what Python calls "ValueError", what JS might call "DomainError" to go with "RangeError", is lame. Allen, Rick, I forget: have we discussed DomainError or ValueError?

# Mark S. Miller (11 years ago)

Is a DomainError the dual of a RangeError?

# Allen Wirfs-Brock (11 years ago)

We did discuss this, as record in ecmascript#224 , and concluded that we it we didn't want to add any new built-in exceptions. Of the existing exceptions , RangeError is closest in concept to what might be described as ValueError.

# Brendan Eich (11 years ago)

Allen Wirfs-Brock wrote:

We did discuss this, as record in ecmascript#224 , and concluded that we it we didn't want to add any new built-in exceptions. Of the existing exceptions , RangeError is closest in concept to what might be described as ValueError.

I'd be ok with adding DomainError -- dual, as Mark says -- cheap one-time addition, not repeated, sold out performance, retired and tax fugitive after ;-).

# Norbert Lindenberg (11 years ago)

I actually had ValueError in the 2011-10-31 draft of ECMA-402; the TC39 meeting on 2011-11-16 decided against that.

globalization:working_draft_ecmascript_globalization_api_2011-10-31.pdf

esdiscuss/2011-November/018507

Norbert

# Norbert Lindenberg (11 years ago)

Correction: The November meeting didn't quite decide; there was more discussion on es-discuss and at the TC39 meeting on 2012-01-19: esdiscuss/2011-November/thread.html#18685, esdiscuss/2012-January/019784

Norbert

# Allen Wirfs-Brock (11 years ago)

On Aug 6, 2013, at 12:40 PM, Brendan Eich wrote:

Allen Wirfs-Brock wrote:

We did discuss this, as record in ecmascript#224 , and concluded that we it we didn't want to add any new built-in exceptions. Of the existing exceptions , RangeError is closest in concept to what might be described as ValueError.

I'd be ok with adding DomainError -- dual, as Mark says -- cheap one-time addition, not repeated, sold out performance, retired and tax fugitive after ;-).

See like a distraction to reopen an issue we already decided.

Also don't really see that it helps much. Right now, API designer have to make a decision about the murky distinction between TypeError (sometimes "type" is interpreted very loosely) and RangeError. Adding DomainError would probably just add to the confusion and in the end it probably makes no difference. Has anybody ever actually seen a JS exception handler that really needs to take conditional action depending upon whether as TypeError or RangeError was thrown?

# Mark Miller (11 years ago)

Even though I contributed the suggestion, I agree with Allen. It is already way too late to pretend that JS is a language in which it is useful to dispatch on the type of error. Given that, let's leave well enough alone and not introduce more error types for any reason other than legacy compat (i.e., with existing DOM practice).

# Brendan Eich (11 years ago)

Allen Wirfs-Brock wrote:

On Aug 6, 2013, at 12:40 PM, Brendan Eich wrote:

Allen Wirfs-Brock wrote:

We did discuss this, as record in ecmascript#224 , and concluded that we it we didn't want to add any new built-in exceptions. Of the existing exceptions , RangeError is closest in concept to what might be described as ValueError. I'd be ok with adding DomainError -- dual, as Mark says -- cheap one-time addition, not repeated, sold out performance, retired and tax fugitive after ;-).

See like a distraction to reopen an issue we already decided.

No, it's called revisiting and we do it because we do make mistakes :-P.

This one is too small to be a mistake, I agree, but your "distraction" word is off-target. TC39 has to revisit decided things now and then. We've done it before as Mark well knows.

Also don't really see that it helps much. Right now, API designer have to make a decision about the murky distinction between TypeError (sometimes "type" is interpreted very loosely) and RangeError. Adding DomainError would probably just add to the confusion and in the end it probably makes no difference.

No, it would be for the input/domain side, as range error should be for output/co-domain, where the type agrees but cannot express a value restriction.

If we don't have a principled approach we will go wrong with RangeError. Possibly we already have?

Has anybody ever actually seen a JS exception handler that really needs to take conditional action depending upon whether as TypeError or RangeError was thrown?

That's not the issue. If you don't care about which (whatever the set of standard error types), you might as well toss a coin. But clearly we have some implicit rule or rules informing when to use RangeError vs. TypeError. Or are the rules explicit?

Saying the Java-esque "Error class hierarchy" is not useful for dispatching also ignores matching in catch clauses, on the Harmony agenda.

But even if we think everything here is a mistake, it is at least an attractive nuisance that trips up developers and other standards' caretakers. We need some ground rules and FAQ entries for when to use Type vs. Range, and why-no-Value/Domain.

# Chad Austin (11 years ago)

On Tue, Aug 6, 2013 at 1:02 PM, Allen Wirfs-Brock <allen at wirfs-brock.com>wrote:

Has anybody ever actually seen a JS exception handler that really needs to take conditional action depending upon whether as TypeError or RangeError was thrown?

For what it's worth, as someone using both promises and custom error types in the field, we do switch on different types of errors. We alternate between instanceof and testing the .name property on the exception as a stylistic choice.

Here's a common idiom (note, I typed this from memory, and haven't compiled it:

fetchServiceResult().then(function(result) { // use result }).catch(function(error) { if (error instanceof NetworkError) { // perhaps retry a couple times // perhaps show a dialog box } else { throw error; // bubble anything else } });

# Anne van Kesteren (11 years ago)

On Tue, Aug 6, 2013 at 10:03 PM, Brendan Eich <brendan at mozilla.com> wrote:

That's not the issue. If you don't care about which (whatever the set of standard error types), you might as well toss a coin. But clearly we have some implicit rule or rules informing when to use RangeError vs. TypeError. Or are the rules explicit?

Saying the Java-esque "Error class hierarchy" is not useful for dispatching also ignores matching in catch clauses, on the Harmony agenda.

But even if we think everything here is a mistake, it is at least an attractive nuisance that trips up developers and other standards' caretakers. We need some ground rules and FAQ entries for when to use Type vs. Range, and why-no-Value/Domain.

And having this sooner rather than later would be welcome. There's new APIs coming up that want to mint new types of errors and it seems we might be stuck with the situation that DOMException.name returns a specific name rather than "DOMException". We can probably still kill DOMError, but the alternative should be clear.

If in the end people are expected to check Error.name I don't think it matters much DOM has a catchall DOMException object that represents a bunch of distinct names.

And FWIW, the distinct names are occasionally useful for certain APIs, e.g. when you want to react differently between a network error and an end-user terminating the operation.

More FWIW, dom.spec.whatwg.org/#errors describes the situation we have today. Timely advice much appreciated.

# Domenic Denicola (11 years ago)

FWIW for non-DOM code I see people using instanceof much more often than checking name.

Thus I personally think that creating a bunch of new DOMException subtypes would be the way to go, e.g. a PermissionDeniedError whose .name is PermissionDeniedError and whose .__proto__ is DOMException.

If we were starting from scratch we'd probably just have a single DOMException (probably named DOMError actually) with a .code property that can vary between types of errors. But since we're not, creating a large hierarchy seems best, since it allows you to preserve all the usual properties of JS errors. The only downside of it is that it creates a large hierarchy, which seems like less of a downside than e.g. having an error whose .name is not equal to its .constructor.name.

# Erik Arvidsson (11 years ago)

On Wed, Aug 21, 2013 at 12:21 PM, Domenic Denicola <domenic at domenicdenicola.com> wrote:

FWIW for non-DOM code I see people using instanceof much more often than checking name.

Thus I personally think that creating a bunch of new DOMException subtypes would be the way to go, e.g. a PermissionDeniedError whose .name is PermissionDeniedError and whose .__proto__ is DOMException.

If we were starting from scratch we'd probably just have a single DOMException (probably named DOMError actually) with a .code property that can vary between types of errors.

.code implies numeric error codes which has gone out of fashion.

# Anne van Kesteren (11 years ago)

On Wed, Aug 21, 2013 at 5:21 PM, Domenic Denicola <domenic at domenicdenicola.com> wrote:

FWIW for non-DOM code I see people using instanceof much more often than checking name.

In public-script-coord and elsewhere people have argued that instanceof is an indication of badness: lists.w3.org/Archives/Public/public-script-coord/2013JulSep/0225.html

Thus I personally think that creating a bunch of new DOMException subtypes would be the way to go, e.g. a PermissionDeniedError whose .name is PermissionDeniedError and whose .__proto__ is DOMException.

That seems like a lot of unnecessary bloat for something that's rather trivial.

If we were starting from scratch we'd probably just have a single DOMException (probably named DOMError actually) with a .code property that can vary between types of errors. But since we're not, creating a large hierarchy seems best, since it allows you to preserve all the usual properties of JS errors. The only downside of it is that it creates a large hierarchy, which seems like less of a downside than e.g. having an error whose .name is not equal to its .constructor.name.

Allen suggested having .subname. But I'm not sure we can still do that and it also seems silly to make these exceptions second-class citizens.