проверка на ошибку typeof в JS

154

В JS не представляется возможным проверить, действительно ли аргумент, переданный функции, имеет тип 'error' или экземпляр Error.

Например, это недопустимо:

typeof err === 'error'

поскольку существует только 6 возможных типов (в виде строк):

Оператор typeof возвращает информацию о типе в виде строки. Есть шесть возможных значений, которые typeofвозвращаются:

"число", "строка", "логическое значение", "объект", "функция" и "неопределенное".

MSDN

Но что, если у меня есть простой пример использования, подобный этому:

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

так каков наилучший способ определить, является ли аргумент экземпляром Error?

является instanceofоператором какой - либо помощи?

Александр Миллс
источник
10
Да, использоватьerr instanceof Error
colecmc
2
@colecmc не будет ли это проблематично, если ошибка может быть из-за кода во фрейме или в другом окне? В этом случае будут разные прототипы объектов Error, и instanceof не будет работать должным образом. (см developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/... )
Играешь
@ Делай, я так не думаю. Это просто подтвердит фактический объект ошибки.
colecmc
1
@colecmc, я думаю, вы обнаружите, что если вы добавили err(скажем) iframe, но затем передаете его родительскому окну для передачи, вы получите (err instanceof Error) === false. Это связано с тем, что у iframe и его родительского окна разные Errorпрототипы объектов. Точно так же объект, как obj = {};will, при передаче функции, выполняющейся в другом окне, yeild (obj instanceof Object)===false. (Что еще хуже, в IE, если вы сохраните ссылку на obj после того, как окно будет разрушено или перемещено, попытка вызвать прототип объекта fns like obj.hasOwnProperty()вызовет ошибки!)
Doin
как вы проверяете тип ошибки, хотя? т.е. catch((err)=>{if (err === ENOENT)возвращает ENOENT is not definedошибку?
старик

Ответы:

262

Вы можете использовать instanceofоператор (но см. Предостережение ниже!).

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

Вышеуказанное не сработает, если ошибка была выдана в другом окне / фрейме / фрейме, отличном от того, где происходит проверка. В этом случае instanceof Errorпроверка вернет false, даже для Errorобъекта. В таком случае, самый простой подход - это печатание на клавиатуре.

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

Тем не менее, типизация утки может привести к ложным срабатываниям, если у вас есть объекты без ошибок, которые содержат stackи messageсвойства.

Trott
источник
1
@ AurélienRibon Отлично работает с ReferenceErrorэкземпляром. Вы проверяете глобальный ReferenceErrorобъект. Попробуйте это: try { foo } catch (e) { console.log(e instanceof Error) }это логи true. Если ReferenceErrorпо какой-то причине вы пытаетесь проверить глобальный объект, вы можете использоватьError.isPrototypeOf(ReferenceError)
Trott
1
@ AurélienRibon (Ух, да, да, не был уверен, что вы говорите «Это не будет работать с ReferenceError», что неверно, или если вы просто указали «Это не будет работать с глобальным объектом ReferenceError») это абсолютно правильно, хотя мне трудно придумать ситуацию, в которой можно было бы проверить это, но это может сказать больше о моей нехватке воображения, чем обоснованность варианта использования).
Тротт
Ха-ха, извините за этих парней, я просто запутался в странной ошибке, где все мои ReferenceErrorэкземпляры не были экземплярами Error. Это произошло из-за вызова vm.runInNewContext()в узле, где все стандартные прототипы переопределяются. Все мои результаты не были экземплярами стандартных объектов. Я искал в SO об этом и попал в эту ветку :)
Aurelien Ribon
1
Вы можете попробоватьconst error = (err instanceof Error || err instanceof TypeError) ? err : new Error(err.message ? err.message : err);
Аамир Африди
Как вы проверите, если instanceofэто определенный тип ошибки, хотя?
старик
25

Я задал оригинальный вопрос - ответ @ Тротта, безусловно, лучший.

Однако, поскольку JS является динамическим языком, а среда выполнения JS так много, instanceofоператор может потерпеть неудачу, особенно при разработке интерфейса, при пересечении границ, таких как iframes. Смотрите: https://github.com/mrdoob/three.js/issues/5886

Если вы в порядке с печатанием утки, это должно быть хорошо:

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

Я лично предпочитаю статически типизированные языки, но если вы используете динамический язык, лучше использовать динамический язык таким, какой он есть, а не заставлять его вести себя как статически типизированный язык.

если вы хотите получить более точную информацию, вы можете сделать это:

   let isError = function(e){
     return e && e.stack && e.message && typeof e.stack === 'string' 
            && typeof e.message === 'string';
    }
Александр Миллс
источник
3
Я предпочитаю эту версию при написании служебной функции или библиотеки. Он не ограничивает пользователей одним конкретным глобальным контекстом, а также правильно обрабатывает десериализованные ошибки из JSON.
хихикать
спасибо, да во многих случаях, если это выглядит как ошибка, мы можем рассматривать как ошибку. в некоторых случаях типирование утки является полностью приемлемым, в некоторых случаях это вообще неприемлемо, в этом случае, совершенно нормально.
Александр Миллс
Я выполнил некоторую отладку с использованием отладчика, и стек и сообщение действительно являются единственными двумя несобственными свойствами в экземплярах Error.
Александр Миллс
1
@Trott, вам может быть интересно узнать о сбое instanceofоператора в некоторых случаях, см. Статью по ссылке .
Александр Миллс
1
Это на самом деле самый простой , так что работает с различными типами ошибок (то есть Error, ReferenceError...). И в моем окружении instanceofнеудачи с треском проваливаются при многих обстоятельствах.
Алексис Вилке
10
var myError = new Error('foo');
myError instanceof Error // true
var myString = "Whatever";
myString instanceof Error // false

Единственная проблема с этим

myError instanceof Object // true

Альтернативой этому было бы использование свойства конструктора.

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

Хотя следует отметить, что это совпадение очень специфично, например:

myError.constructor === TypeError // false
Том
источник
2

Вы можете Object.prototype.toStringлегко проверить, является ли объект объектом Error, который также будет работать для разных кадров.

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
источник
1

Вы можете использовать obj.constructor.name для проверки «класса» объекта https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Function/name#Function_names_in_classes

Например

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

В приведенной выше строке будет записано «Ошибка», которая является именем класса объекта. Это может быть использовано с любыми классами в javascript, если класс не использует свойство, которое называется «имя»

persistent_poltergeist
источник
Это ненадежно при минимизации кода и использовании подклассов ошибок
felixfbecker
1

Спасибо @Trott за ваш код, я просто использовал тот же код и добавил рабочий пример в реальном времени для пользы других.

<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

Или используйте это для различных типов ошибок

function isError(val) {
  return (!!val && typeof val === 'object')
    && ((Object.prototype.toString.call(val) === '[object Error]')
      || (typeof val.message === 'string' && typeof val.name === 'string'))
}
Крис
источник