vérification du type d'erreur dans JS


154

Dans JS, il ne semble pas possible de vérifier si un argument passé à une fonction est en fait du type "error" ou une instance de Error.

Par exemple, ce n'est pas valide:

typeof err === 'error'

puisqu'il n'y a que 6 types possibles (sous forme de chaînes):

L'opérateur typeof renvoie les informations de type sous forme de chaîne. Il y a six valeurs possibles qui typeofretournent:

"nombre", "chaîne", "booléen", "objet", "fonction" et "indéfini".

MSDN

Mais que faire si j'ai un cas d'utilisation simple comme celui-ci:

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);
    }
}

Alors, quelle est la meilleure façon de déterminer si un argument est une instance d'Erreur?

l' instanceofopérateur est-il d'une aide quelconque?


10
Oui, utilisezerr instanceof Error
colecmc

2
@colecmc ne sera-t-il pas problématique si l'erreur peut provenir de code dans un cadre ou une autre fenêtre? Dans ce cas, il y aura différents objets Error prototypes et instanceof ne fonctionnera pas comme prévu. (voir developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/… )
Fait

@Doin, je ne pense pas. Il validera simplement l'objet d'erreur réel.
colecmc

1
@colecmc, je pense que vous constaterez que si vous avez jeté err(disons) une iframe, mais que vous la passez ensuite à la fenêtre parent pour la remise, vous obtiendrez (err instanceof Error) === false. C'est parce que l'iframe et sa fenêtre parente ont des Errorprototypes d'objets distincts et différents . De même, un objet comme obj = {};will, lorsqu'il est passé à une fonction s'exécutant dans une fenêtre différente, yeild (obj instanceof Object)===false. (Pire encore, dans IE, si vous conservez une référence à obj après que sa fenêtre soit détruite ou parcourue, essayer d'appeler des objets prototypes fns comme obj.hasOwnProperty()cela provoquera des erreurs!)
Doin

comment vérifiez-vous le type d'erreur tho? c'est-à-dire catch((err)=>{if (err === ENOENT)renvoie une ENOENT is not definederreur?
oldboy

Réponses:


262

Vous pouvez utiliser l' instanceofopérateur (mais voir la mise en garde ci-dessous!).

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

Ce qui précède ne fonctionnera pas si l'erreur a été lancée dans une fenêtre / un cadre / une iframe différente de celle où la vérification a lieu. Dans ce cas, la instanceof Errorvérification renverra false, même pour un Errorobjet. Dans ce cas, l'approche la plus simple est le typage canard.

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

Cependant, le typage canard peut produire des faux positifs si vous avez des objets sans erreur qui contiennent stacket des messagepropriétés.


1
@ AurélienRibon Cela fonctionne très bien avec une ReferenceErrorinstance. Vous vérifiez l' ReferenceErrorobjet global . Essayez ceci: try { foo } catch (e) { console.log(e instanceof Error) }il enregistre true. Si vous essayez de vérifier l' ReferenceErrorobjet global pour une raison quelconque, vous pouvez utiliserError.isPrototypeOf(ReferenceError)
Trott

1
@ AurélienRibon (Euh, aussi, oui, je ne savais pas si vous disiez "Cela ne fonctionnera pas avec une ReferenceError", ce qui est incorrect, ou si vous signaliez simplement "Cela ne fonctionnera pas avec l'objet ReferenceError global" ce qui est tout à fait correct, même si j'ai du mal à trouver une situation où l'on vérifierait cela, mais cela peut en dire plus sur mon manque d'imagination que sur la validité du cas d'utilisation).
Trott

Haha, désolé pour ces gars, j'étais juste enfermé dans une erreur étrange dont toutes mes ReferenceErrorinstances n'étaient pas des instances Error. Cela était dû à un appel à vm.runInNewContext()in node, où tous les prototypes standard sont redéfinis. Tous mes résultats n'étaient pas des instances d'objets standards. Je cherchais dans SO à ce sujet, et je suis tombé dans ce fil :)
Aurelien Ribon

1
Vous pouvez essayerconst error = (err instanceof Error || err instanceof TypeError) ? err : new Error(err.message ? err.message : err);
Aamir Afridi

Comment vérifier si c'est instanceofun type d'erreur spécifique?
oldboy

25

J'ai posé la question initiale - la réponse de @ Trott est sûrement la meilleure.

Cependant, avec JS étant un langage dynamique et avec un si grand nombre d'environnements d'exécution JS, l' instanceofopérateur peut échouer en particulier dans le développement frontal lorsqu'il franchit des limites telles que les iframes. Voir: https://github.com/mrdoob/three.js/issues/5886

Si vous êtes d'accord avec la saisie de canard, cela devrait être bon:

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

Personnellement, je préfère les langages à typage statique, mais si vous utilisez un langage dynamique, il est préférable d'adopter un langage dynamique tel qu'il est, plutôt que de le forcer à se comporter comme un langage à typage statique.

si vous voulez être un peu plus précis, vous pouvez faire ceci:

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

3
Je préfère cette version lors de l'écriture d'une fonction utilitaire ou d'une bibliothèque. Il ne contraint pas les utilisateurs à un contexte global particulier et gère également correctement les erreurs désérialisées de JSON.
snickle

merci, ouais dans de nombreux cas, si cela ressemble à une erreur, nous pouvons la traiter comme une erreur. dans certains cas, le typage du canard est totalement acceptable, dans certains cas, ce n'est pas du tout acceptable, dans ce cas, parfaitement bien.
Alexander Mills

J'ai effectué un débogage à l'aide d'un débogueur, et la pile et le message semblent vraiment être les deux seules propriétés non natives sur les instances Error.
Alexander Mills

1
@Trott, vous pourriez être intéressé de savoir si l' instanceofopérateur échoue dans certains cas, consultez l'article lié.
Alexander Mills

1
C'est en fait le plus simple, donc cela fonctionne avec les différents types d'erreurs (c'est Error-à- dire,, ReferenceError...). Et dans mon environnement, cela instanceoféchoue lamentablement dans de nombreuses circonstances.
Alexis Wilke

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

Le seul problème avec ceci est

myError instanceof Object // true

Une alternative à cela serait d'utiliser la propriété constructeur.

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

Bien qu'il soit à noter que ce match est très spécifique, par exemple:

myError.constructor === TypeError // false

2

Vous pouvez utiliser Object.prototype.toStringpour vérifier facilement si un objet est un Error, qui fonctionnera également pour différents cadres.

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


1

Vous pouvez utiliser obj.constructor.name pour vérifier la "classe" d'un objet https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Function/name#Function_names_in_classes

Par exemple

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

La ligne ci-dessus enregistrera "Erreur" qui est le nom de classe de l'objet. Cela pourrait être utilisé avec n'importe quelle classe en javascript, si la classe n'utilise pas une propriété qui porte le nom "nom"


Ce n'est pas fiable lors de la réduction du code et de l'utilisation de sous
felixfbecker

1

Merci @Trott pour votre code, j'ai juste utilisé le même code et ajouté avec un exemple de travail en temps réel pour le bénéfice des autres.

<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>


0

Ou utilisez ceci pour différents types d'erreurs

function isError(val) {
  return (!!val && typeof val === 'object')
    && ((Object.prototype.toString.call(val) === '[object Error]')
      || (typeof val.message === 'string' && typeof val.name === 'string'))
}
En utilisant notre site, vous reconnaissez avoir lu et compris notre politique liée aux cookies et notre politique de confidentialité.
Licensed under cc by-sa 3.0 with attribution required.