javascript - comprobando el tipo de error en JS
En JS no parece posible comprobar si un argumento pasado a una función es realmente del tipo 'error' o una instancia de Error.
Por ejemplo, esto no es válido:
typeof err === 'error'ya que solo hay 6 tipos posibles (en forma de cadenas):
El operador typeof devuelve información del tipo como una cadena. Hay seis valores posibles que typeof devuelve:
"número", "cadena", "booleano", "objeto", "función" y "indefinido".
MSDN
Pero, ¿y si tengo un caso de uso simple como este:
function errorHandler(err) {
if (typeof err === 'error') {
throw err;
}
else {
console.error('Unexpectedly, no error was passed to error handler. But here is the message:',err);
}
}
¿Cuál es la mejor manera de determinar si un argumento es una instancia de Error?
¿Es el operador instanceof de alguna ayuda?
Respuesta #1
Puedes usar el operador instanceof (¡pero mira la advertencia abajo!).
var myError = new Error('foo');
myError instanceof Error // true
var myString = "Whatever";
myString instanceof Error // false
Lo anterior no funcionará si el error se lanzó en una ventana/trama/iframe diferente de donde se está realizando la verificación. En ese caso, la comprobación instanceof Error devolverá falso, incluso para un objeto Error. En ese caso, el enfoque más sencillo es el duck-typing.
if (myError && myError.stack && myError.message) {
// it's an error, probably
}
Sin embargo, el duck-typing puede producir falsos positivos si tienes objetos que no son errores pero que contienen propiedades stack y message.
Respuesta #2
Hice la pregunta original: la respuesta de @Trott seguramente es la mejor.
Sin embargo, al ser JS un lenguaje dinámico y al existir tantos entornos de ejecución de JS, el operador instanceof puede fallar, especialmente en el desarrollo front-end al cruzar límites como iframes. Véase: https://github.com/mrdoob/three.js/issues/5886
Si estás de acuerdo con el duck typing, esto debería estar bien:
let isError = function(e){
return e && e.stack && e.message;
};
Personalmente prefiero los lenguajes tipados estáticamente, pero si estás usando un lenguaje dinámico, es mejor aceptar un lenguaje dinámico por lo que es, en lugar de obligarlo a comportarse como un lenguaje tipado estáticamente.
si quisieras ser un poco más preciso, podrías hacer esto:
let isError = (e) => {
return e &&
e.stack &&
e.message &&
typeof e.stack === 'string' &&
typeof e.message === 'string';
};
Respuesta #3
Puedes usar Object.prototype.toString para comprobar fácilmente si un objeto es un Error, lo cual funcionará también para diferentes marcos.
function isError(obj){
return Object.prototype.toString.call(obj) === "[object Error]";
}
function isError(obj){
return Object.prototype.toString.call(obj) === "[object Error]";
}
console.log("Error:", isError(new Error));
console.log("RangeError:", isError(new RangeError));
console.log("SyntaxError:", isError(new SyntaxError));
console.log("Object:", isError({}));
console.log("Array:", isError([]));Este comportamiento está garantizado por la Especificación del Lenguaje ECMAScript.
Object.prototype.toString:
Cuando se llama al método toString, se realizan los siguientes pasos:
- Si este valor es indefinido, devuelve "[object Undefined]".
- Si este valor es nulo, devuelve "[object Null]".
- Sea O el resultado de llamar a ToObject pasando el valor this como argumento.
- Sea clase el valor de la propiedad interna [[Class]] de O.
- Devuelve el valor de cadena que es el resultado de concatenar las tres cadenas "[object ", class, y "]".
Propiedades de las instancias de error:
Las instancias de Error heredan propiedades del objeto prototipo Error y su valor de propiedad interna
[[Class]]es "Error". Las instancias de Error no tienen propiedades especiales.
Respuesta #4
Solución TypeScript
Puedes definir un guardia de tipo definido por el usuario, simplemente necesitas definir una función cuyo tipo de retorno sea un predicado de tipo
Puedes comprobar si una variable es un error así
const isError = (err: unknown): err is Error => err instanceof Error;
entonces dentro de try catch valida así
try {
login(username, password);
} catch (err) {
if (isError(err)) {
console.log(err.message);
}
Respuesta #5
var myError = new Error('foo');
myError instanceof Error // true
var myString = "Whatever";
myString instanceof Error // false
El único problema con esto es
myError instanceof Object // true
Una alternativa a esto sería usar la propiedad constructor.
myError.constructor === Object // false
myError.constructor === String // false
myError.constructor === Boolean // false
myError.constructor === Symbol // false
myError.constructor === Function // false
myError.constructor === Error // true
Aunque cabe señalar que este partido es muy específico, por ejemplo:
myError.constructor === TypeError // false
