如何在 switch 语句中使用 instanceof

我使用自定义错误(错误)来处理基于类的错误,如下所示:

import { DatabaseEntryNotFoundError, NotAllowedError } from 'customError';


function fooRoute(req, res) {
doSomethingAsync()
.then(() => {
// on resolve / success
return res.send(200);
})
.catch((error) => {
// on reject / failure
if (error instanceof DatabaseEntryNotFoundError) {
return res.send(404);
} else if (error instanceof NotAllowedError) {
return res.send(400);
}
log('Failed to do something async with an unspecified error: ', error);
return res.send(500);
};
}

现在我更愿意使用一个开关来实现这种类型的流,结果是:

import { DatabaseEntryNotFoundError, NotAllowedError } from 'customError';


function fooRoute(req, res) {
doSomethingAsync()
.then(() => {
// on resolve / success
return res.send(200);
})
.catch((error) => {
// on reject / failure
switch (error instanceof) {
case NotAllowedError:
return res.send(400);
case DatabaseEntryNotFoundError:
return res.send(404);
default:
log('Failed to do something async with an unspecified error: ', error);
return res.send(500);
}
});
}

Instanceof 不是这样工作的,所以后者失败了。

有没有办法在 switch 语句中检查一个实例的类?

57429 次浏览

A good option is to use the constructor property of the object:

// on reject / failure
switch (error.constructor) {
case NotAllowedError:
return res.send(400);
case DatabaseEntryNotFoundError:
return res.send(404);
default:
log('Failed to do something async with an unspecified error: ', error);
return res.send(500);
}

Notice that the constructor must match exactly with the one that object was created (suppose error is an instance of NotAllowedError and NotAllowedError is a subclass of Error):

  • error.constructor === NotAllowedError is true
  • error.constructor === Error is false

This makes a difference from instanceof, which can match also the super class:

  • error instanceof NotAllowedError is true
  • error instanceof Error is true

Check this interesting post about constructor property.

An alternative to this switch case is to just have a status field in the Error's constructor.

For Example, build your error like so:

class NotAllowedError extends Error {
constructor(message, status) {
super(message);
this.message = message;
this.status = 403; // Forbidden error code
}
}

Handle your error like so:

.catch((error) => {
res.send(error.status);
});

Workaround, to avoid if-else. Found here

switch (true) {
case error instanceof NotAllowedError:
return res.send(400);


case error instanceof DatabaseEntryNotFoundError:
return res.send(404);


default:
log('Failed to do something async with an unspecified error: ', error);
return res.send(500);
}

I am unable to write comments so i had to write an answer.

When using constructor object to determine instance type of an object, one should keep in mind the following paragraph from Mozilla developer web docs:

There is nothing protecting the constructor property from being re-assigned or shadowed, so using it to detect the type of a variable should usually be avoided in favor of less fragile ways like instanceof and Symbol.toStringTag for objects, or typeof for primitives.

Another concern when using constructor object is if later you are going to create some new subclasses of already existing classes. Because when checking type this way it doesn't detect objects of subclasses as instances of parent class.

Having said that, this option does provide a nice way to determine object type within switch statement, as long as you aware of risks when doing so.

Why not just use the error name?

class CustomError extends Error {
constructor(message) {
super(message);
this.name = "CustomError";
}
}

Then:

switch(error.name) {
case: CustomError.name:
// Custom error handler.
break;


default
// Default error handler.
break;
}