verificando o erro typeof em JS

153

Em JS, não parece possível verificar se um argumento transmitido para uma função é realmente do tipo 'error' ou uma instância de Error.

Por exemplo, isso não é válido:

typeof err === 'error'

pois existem apenas 6 tipos possíveis (na forma de cadeias):

O operador typeof retorna informações de tipo como uma sequência. Existem seis valores possíveis que typeofretornam:

"number", "string", "boolean", "object", "function" e "undefined".

MSDN

Mas e se eu tiver um caso de uso simples 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);
    }
}

então, qual é a melhor maneira de determinar se um argumento é uma instância de Error?

é o instanceofoperador de alguma ajuda?

Alexander Mills
fonte
10
Sim, useerr instanceof Error
colecmc
2
@colecmc não será problemático se o erro puder ter vindo do código em um quadro ou em outra janela? Nesse caso, haverá diferentes protótipos de objetos Error, e instanceof não funcionará conforme o esperado. (consulte developer.mozilla.org/pt-BR/docs/Web/JavaScript/Reference/… )
Doin em
@ Doin, acho que não. Apenas validará o objeto de erro real.
colecmc
1
@colecmc, acho que você descobrirá que, se você jogou err(digamos) um iframe, mas depois o passa para a janela principal para entrega, você receberá (err instanceof Error) === false. Isso ocorre porque o iframe e sua janela pai têm Errorprotótipos de objetos distintos e diferentes . Da mesma forma, um objeto como obj = {};vontade, quando passado para uma função executando em uma janela diferente, também será (obj instanceof Object)===false. (Mesmo pior, no IE, se você manter uma referência para obj após a sua janela é destruída ou navegado, tentando chamar FNS objeto de protótipo como obj.hasOwnProperty()irá lançar erros!)
Doin
Como você verifica o tipo de erro? ou seja, catch((err)=>{if (err === ENOENT)retorna ENOENT is not definederro?
Oldboy

Respostas:

261

Você pode usar o instanceofoperador (mas veja a advertência abaixo!).

var myError = new Error('foo');
myError instanceof Error // true
var myString = "Whatever";
myString instanceof Error // false

O procedimento acima não funcionará se o erro foi gerado em uma janela / quadro / iframe diferente daquele em que a verificação está ocorrendo. Nesse caso, a instanceof Errorverificação retornará false, mesmo para um Errorobjeto. Nesse caso, a abordagem mais fácil é a digitação de patos.

if (myError && myError.stack && myError.message) {
  // it's an error, probably
}

No entanto, pato digitação pode produzir falsos positivos, se você tem objetos não-erro que contêm stacke messagepropriedades.

Trott
fonte
1
@ AurélienRibon Funciona muito bem com uma ReferenceErrorinstância. Você está verificando o ReferenceErrorobjeto global . Tente o seguinte: try { foo } catch (e) { console.log(e instanceof Error) }Registra true. Se você está tentando verificar o ReferenceErrorobjeto global por algum motivo, pode usá-loError.isPrototypeOf(ReferenceError)
188 Trott
1
@ AurélienRibon (Ah, também, sim, não tinha certeza se você estava dizendo "Isso não funcionará com um ReferenceError" que está incorreto ou se você estava apenas apontando "Isso não funcionará com o objeto ReferenceError global" o que é absolutamente correto, embora eu esteja tendo dificuldades para encontrar uma situação em que alguém possa verificar isso, mas isso pode dizer mais sobre minha falta de imaginação do que a validade do caso de uso).
Trott 18/09
Haha, desculpe por esses caras, eu estava apenas preso em um erro estranho, onde ReferenceErrornem todas as minhas instâncias eram Error. Isso ocorreu devido a uma chamada vm.runInNewContext()no nó, onde todos os protótipos padrão são redefinidos. Todos os meus resultados não foram instâncias de objetos padrão. Eu estava olhando em SO sobre isso, e caiu em esta discussão :)
Aurelien Ribon
1
Você pode tentarconst error = (err instanceof Error || err instanceof TypeError) ? err : new Error(err.message ? err.message : err);
Aamir Afridi
Como você verifica se é um instanceoftipo específico de erro?
oldboy 30/10/19
25

Eu fiz a pergunta original - a resposta de @ Trott é certamente a melhor.

No entanto, com o JS sendo uma linguagem dinâmica e com muitos ambientes de tempo de execução JS, o instanceofoperador pode falhar, especialmente no desenvolvimento de front-end ao cruzar limites, como iframes. Consulte: https://github.com/mrdoob/three.js/issues/5886

Se você concorda com a digitação do pato, isso deve ser bom:

let isError = function(e){
 return e && e.stack && e.message;
}

Pessoalmente, prefiro idiomas de tipo estaticamente, mas se você estiver usando um idioma dinâmico, é melhor adotar um idioma dinâmico para o que é, em vez de forçá-lo a se comportar como um idioma de tipo estaticamente.

se você quiser ser um pouco mais preciso, faça o seguinte:

   let isError = function(e){
     return e && e.stack && e.message && typeof e.stack === 'string' 
            && typeof e.message === 'string';
    }
Alexander Mills
fonte
3
Eu prefiro esta versão ao escrever uma função ou biblioteca de utilidade. Ele não restringe os usuários a um contexto global específico e também lida adequadamente com erros desserializados do JSON.
snickle
obrigado, sim, em muitos casos, se parecer um erro, podemos tratar como um erro. em alguns casos, a digitação com patos é totalmente aceitável; em alguns casos, não é aceitável; neste caso, perfeitamente bem.
Alexander Mills
Fiz algumas depurações usando um depurador e a pilha e a mensagem realmente parecem ser as únicas duas propriedades não nativas nas instâncias de erro.
Alexander Mills
1
@Trott você pode estar interessado em saber sobre a instanceoffalha do operador em alguns casos, consulte o artigo vinculado.
Alexander Mills
1
Este é realmente o mais simples para que ele funciona com os diferentes tipos de erros (ou seja Error, ReferenceError...). E, no meu ambiente, a instanceoffalha falha miseravelmente em muitas circunstâncias.
Alexis Wilke
10
var myError = new Error('foo');
myError instanceof Error // true
var myString = "Whatever";
myString instanceof Error // false

O único problema com isso é

myError instanceof Object // true

Uma alternativa para isso seria usar a propriedade construtor.

myError.constructor === Object // false
myError.constructor === String // false
myError.constructor === Boolean // false
myError.constructor === Symbol // false
myError.constructor === Function // false
myError.constructor === Error // true

Embora seja necessário observar que essa correspondência é muito específica, por exemplo:

myError.constructor === TypeError // false
Tom
fonte
2

Você pode usar Object.prototype.toStringpara verificar facilmente se um objeto é um Error, o que também funcionará para quadros diferentes.

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([]));

hev1
fonte
1

Você pode usar obj.constructor.name para verificar a "classe" de um objeto https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Function/name#Function_names_in_classes

Por exemplo

var error = new Error("ValidationError");
console.log(error.constructor.name);

A linha acima registrará "Erro", que é o nome da classe do objeto. Isso pode ser usado com qualquer classe em javascript, se a classe não estiver usando uma propriedade com o nome "name"

persistent_poltergeist
fonte
Este não é confiável quando minifying código e usando subclasses de erro
felixfbecker
1

Obrigado @Trott pelo seu código, usei o mesmo código e adicionei um exemplo de trabalho em tempo real para o benefício de outras pessoas.

<html>
<body >

<p>The **instanceof** operator returns true if the specified object is an instance of the specified object.</p>



<script>
	var myError = new Error("TypeError: Cannot set property 'innerHTML' of null"); // error type when element is not defined
	myError instanceof Error // true
	
	
	
	
	function test(){
	
	var v1 = document.getElementById("myid").innerHTML ="zunu"; // to change with this
	
	try {
		  var v1 = document.getElementById("myidd").innerHTML ="zunu"; // exception caught
		  } 
		  
	catch (e) {
		  if (e instanceof Error) {
			console.error(e.name + ': ' + e.message) // error will be displayed at browser console
		  }
  }
  finally{
		var v1 = document.getElementById("myid").innerHTML ="Text Changed to Zunu"; // finally innerHTML changed to this.
	}
	
	}
	
</script>
<p id="myid">This text will change</p>
<input type="button" onclick="test();">
</body>
</html>

Zabi Baig
fonte
0

Ou use-o para diferentes tipos de erros

function isError(val) {
  return (!!val && typeof val === 'object')
    && ((Object.prototype.toString.call(val) === '[object Error]')
      || (typeof val.message === 'string' && typeof val.name === 'string'))
}
Chris
fonte