sprawdzanie typu błędu w JS

153

W JS nie wydaje się możliwe sprawdzenie, czy argument przekazany do funkcji jest rzeczywiście typu „błąd”, czy też wystąpieniem błędu.

Na przykład to nie jest prawidłowe:

typeof err === 'error'

ponieważ istnieje tylko 6 możliwych typów (w postaci ciągów):

Operator typeof zwraca informacje o typie w postaci ciągu. Istnieje sześć możliwych wartości, które typeofzwracają:

„liczba”, „ciąg znaków”, „wartość logiczna”, „obiekt”, „funkcja” i „nieokreślona”.

MSDN

Ale co, jeśli mam taki prosty przypadek użycia:

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

więc jaki jest najlepszy sposób określenia, czy argument jest wystąpieniem błędu?

czy instanceofoperator ma jakąś pomoc?

Alexander Mills
źródło
10
Tak, użyjerr instanceof Error
colecmc
2
@colecmc czy nie będzie to problematyczne, jeśli błąd mógł pochodzić z kodu w ramce lub innym oknie? W takim przypadku będą różne prototypowe obiekty Error, a instanceof nie będzie działał zgodnie z oczekiwaniami. (patrz developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/… )
Doin
@Doin, nie sądzę. Po prostu zweryfikuje rzeczywisty obiekt błędu.
colecmc
1
@colecmc, myślę, że przekonasz się, że jeśli wrzuciłeś err(powiedzmy) ramkę iframe, a następnie przekażesz ją do okna nadrzędnego w celu przekazania, otrzymasz (err instanceof Error) === false. Dzieje się tak, ponieważ element iframe i jego okno nadrzędne mają różne, różne Errorprototypy obiektów. Podobnie obiekt taki jak obj = {};will, przekazany do funkcji działającej w innym oknie, yeild (obj instanceof Object)===false. (Co gorsza, w IE, jeśli zachowasz odniesienie do obj po zniszczeniu lub nawigacji w jego oknie, próba wywołania prototypu obiektu fns, takiego jak, obj.hasOwnProperty()spowoduje wyświetlenie błędów!)
Doin
jak sprawdzić rodzaj błędu mimo? czyli catch((err)=>{if (err === ENOENT)zwraca ENOENT is not definedbłąd?
oldboy,

Odpowiedzi:

261

Możesz użyć instanceofoperatora (ale zobacz zastrzeżenie poniżej!).

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

Powyższe nie zadziała, jeśli błąd został zgłoszony w innym oknie / ramce / ramce iframe niż ta, w której odbywa się sprawdzanie. W takim przypadku instanceof Errorsprawdzenie zwróci fałsz, nawet dla Errorobiektu. W takim przypadku najłatwiejszym podejściem jest pisanie na klawiaturze.

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

Jednak pisanie typu kaczego może generować fałszywe alarmy, jeśli masz obiekty bez błędów, które zawierają stacki messagewłaściwości.

Trott
źródło
1
@ AurélienRibon Działa dobrze z ReferenceErrorinstancją. Sprawdzasz ReferenceErrorobiekt globalny . Spróbuj tego: try { foo } catch (e) { console.log(e instanceof Error) }rejestruje true. Jeśli ReferenceErrorz jakiegoś powodu próbujesz sprawdzić obiekt globalny , możesz użyćError.isPrototypeOf(ReferenceError)
Trott
1
@ AurélienRibon (Uh, również tak, nie byłeś pewien, czy mówisz „To nie zadziała z ReferenceError”, co jest niepoprawne, czy po prostu wskazywałeś „To nie zadziała z globalnym obiektem ReferenceError” co jest absolutnie poprawne, chociaż trudno mi wymyślić sytuację, w której można by to sprawdzić, ale to może powiedzieć więcej o moim braku wyobraźni niż o słuszności przypadku użycia).
Trott,
Haha, przepraszam za tych facetów, zostałem zamknięty w dziwnym błędzie, w którym wszystkie moje ReferenceErrorinstancje nie były instancjami Error. Było to spowodowane wezwaniem do vm.runInNewContext()w węźle, w którym wszystkie standardowe prototypy są przedefiniowane. Wszystkie moje wyniki nie były instancjami standardowych obiektów. Szukałem w SO o tym i wpadłem w ten wątek :)
Aurelien Ribon
1
Możesz spróbowaćconst error = (err instanceof Error || err instanceof TypeError) ? err : new Error(err.message ? err.message : err);
Aamir Afridi
jak sprawdzić, czy jest instanceofto określony rodzaj błędu?
oldboy
25

Zadałem oryginalne pytanie - odpowiedź @ Trott jest z pewnością najlepsza.

Jednak ze względu na to, że JS jest językiem dynamicznym i jest tak wiele środowisk wykonawczych JS, instanceofoperator może zawieść, szczególnie w przypadku programowania front-end, gdy przekracza granice, takie jak ramki iframe. Zobacz: https://github.com/mrdoob/three.js/issues/5886

Jeśli nie przeszkadza ci pisanie kaczkami, to powinno być dobre:

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

Osobiście wolę języki statyczne, ale jeśli używasz języka dynamicznego, najlepiej jest objąć język dynamiczny takim, jakim jest, zamiast zmuszać go do zachowywania się jak język z typowaniem statycznym.

jeśli chcesz uzyskać dokładniejsze informacje, możesz zrobić to:

   let isError = function(e){
     return e && e.stack && e.message && typeof e.stack === 'string' 
            && typeof e.message === 'string';
    }
Alexander Mills
źródło
3
Preferuję tę wersję podczas pisania funkcji narzędzia lub biblioteki. Nie ogranicza użytkowników do jednego konkretnego kontekstu globalnego, a także poprawnie obsługuje deserializowane błędy z formatu JSON.
snickle
dzięki, tak w wielu przypadkach, jeśli wygląda to na błąd, możemy traktować jak błąd. w niektórych przypadkach pisanie na klawiaturze typu kaczka jest całkowicie akceptowalne, w niektórych przypadkach w ogóle nie do zaakceptowania, w tym przypadku całkowicie w porządku.
Alexander Mills
Zrobiłem trochę debugowania za pomocą debugera, a stos i komunikat naprawdę wydają się być jedynymi dwoma nienatywnymi właściwościami w wystąpieniach błędów.
Alexander Mills
1
@Trott Możesz być zainteresowany, aby wiedzieć, że instanceofoperator w niektórych przypadkach nie działa, zobacz powiązany artykuł.
Alexander Mills,
1
Jest to rzeczywiście najprostszy, dlatego współpracuje z różnymi typami błędów (to znaczy Error, ReferenceError...). A w moim środowisku instanceofw wielu okolicznościach zawodzi.
Alexis Wilke
10
var myError = new Error('foo');
myError instanceof Error // true
var myString = "Whatever";
myString instanceof Error // false

Jedyny problem z tym jest

myError instanceof Object // true

Alternatywą byłoby użycie właściwości 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

Chociaż należy zauważyć, że to dopasowanie jest bardzo specyficzne, na przykład:

myError.constructor === TypeError // false
Tomek
źródło
2

Możesz użyć, Object.prototype.toStringaby łatwo sprawdzić, czy obiekt jest Error, który będzie działał również dla różnych ramek.

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
źródło
1

Możesz użyć obj.constructor.name, aby sprawdzić „klasę” obiektu https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Function/name#Function_names_in_classes

Na przykład

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

Powyższa linia zarejestruje „Błąd”, który jest nazwą klasy obiektu. Można tego użyć z dowolnymi klasami w javascript, jeśli klasa nie używa właściwości o nazwie „nazwa”

persistent_poltergeist
źródło
To nie jest niezawodne, gdy minifying kod i przy użyciu podklasy błędach
felixfbecker
1

Dzięki @Trott za twój kod, właśnie użyłem tego samego kodu i dodałem przykład pracy w czasie rzeczywistym dla dobra innych.

<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
źródło
0

Lub użyj tego do różnych typów błędów

function isError(val) {
  return (!!val && typeof val === 'object')
    && ((Object.prototype.toString.call(val) === '[object Error]')
      || (typeof val.message === 'string' && typeof val.name === 'string'))
}
Chris
źródło