ค้นหาเว็บไซต์

javascript - การตรวจสอบ typeof error ใน JS


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

แล้ววิธีที่ดีที่สุดในการกำหนดว่าข้อโต้แย้งเป็นตัวอย่างของข้อผิดพลาดคืออะไร?

ตัวดำเนินการ instanceof มีประโยชน์อะไรไหม?

คำตอบ #1

คุณสามารถใช้ตัวดำเนินการ instanceof ได้ (แต่ดูข้อควรระวังด้านล่าง!)

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

วิธีข้างต้นจะใช้ไม่ได้ถ้าข้อผิดพลาดเกิดขึ้นในหน้าต่าง/กรอบ/iframe ที่แตกต่างจากที่มีการตรวจสอบ ในกรณีนั้น การตรวจสอบ instanceof Error จะคืนค่าเป็น false แม้กระทั่งสำหรับอ็อบเจ็กต์ Error ในกรณีนั้น วิธีที่ง่ายที่สุดคือการใช้ duck-typing

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

อย่างไรก็ตาม การใช้ duck-typing อาจทำให้เกิดผลบวกเทียมได้หากคุณมีวัตถุที่ไม่ใช่ข้อผิดพลาดแต่มีคุณสมบัติ stack และ message

คำตอบ #2

ฉันถามคำถามเดิม - คำตอบของ @Trott แน่นอนว่าเป็นคำตอบที่ดีที่สุด

อย่างไรก็ตาม ด้วย JS เป็นภาษาที่มีความยืดหยุ่นและมีสภาพแวดล้อมการรัน JS จำนวนมาก ตัวดำเนินการ instanceof อาจล้มเหลวโดยเฉพาะในการพัฒนาแบบ front-end เมื่อข้ามเขตแดนเช่น iframes ดูที่: https://github.com/mrdoob/three.js/issues/5886

ถ้าคุณโอเคกับ duck typing แบบนั้น ก็น่าจะใช้ได้:

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

ฉันส่วนตัวชอบภาษาที่มีการกำหนดชนิดตัวแปรแบบคงที่ แต่ถ้าคุณกำลังใช้ภาษาที่มีการกำหนดชนิดตัวแปรแบบไดนามิก ควรยอมรับภาษานั้นในแบบที่มันเป็น มากกว่าพยายามบังคับให้มันทำงานเหมือนภาษาที่มีการกำหนดชนิดตัวแปรแบบคงที่

ถ้าคุณต้องการให้แม่นยำขึ้นอีกนิด คุณสามารถทำแบบนี้ได้:

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

คำตอบ #3

คุณสามารถใช้ 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([]));

พฤติกรรมนี้ได้รับการรับประกันโดยข้อกำหนดของภาษา ECMAScript

Object.prototype.toString:

เมื่อมีการเรียกใช้เมธอด toString ขั้นตอนดังต่อไปนี้จะถูกดำเนินการ:

  1. ถ้าค่าตัวนี้ไม่ถูกกำหนด ให้คืนค่า "[object Undefined]".
  2. ถ้าค่าตัวนี้เป็นค่าว่าง ให้คืนค่า "[object Null]".
  3. ให้ O เป็นผลลัพธ์ของการเรียกใช้ ToObject โดยส่งค่า this เป็นอาร์กิวเมนต์
  4. ให้ class เป็นค่าของสมบัติภายใน [[Class]] ของ O
  5. ส่งค่าของสตริงที่ได้มาจากการรวมสตริงทั้งสาม "[object ", class, และ "]"

คุณสมบัติของตัวอย่างข้อผิดพลาด:

อินสแตนซ์ของ Error สืบทอดคุณสมบัติจากอ็อบเจ็กต์ต้นแบบของ Error และค่าคุณสมบัติภายใน [[Class]] ของพวกมันคือ "Error" อินสแตนซ์ของ Error ไม่มีคุณสมบัติพิเศษใด ๆ

คำตอบ #4

โซลูชัน TypeScript

คุณสามารถกำหนดตัวป้องกันประเภทที่ผู้ใช้กำหนดเองได้ เพียงแค่ต้องกำหนดฟังก์ชันที่ชนิดการคืนค่าของมันเป็นตัวทำนายประเภท

คุณสามารถตรวจสอบได้ว่าตัวแปรเป็นข้อผิดพลาดแบบนี้

const isError = (err: unknown): err is Error => err instanceof Error;

แล้วภายใน try catch ให้ตรวจสอบว่ามีลักษณะเช่นนี้

try {
  login(username, password);
} catch (err) {
  if (isError(err)) {
  console.log(err.message);
}

คำตอบ #5

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

ปัญหาเดียวกับเรื่องนี้คือ

myError instanceof Object // true

ทางเลือกหนึ่งสำหรับสิ่งนี้คือการใช้คุณสมบัติ 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

แม้ว่าควรสังเกตว่าการจับคู่นี้เฉพาะมาก ตัวอย่างเช่น:

myError.constructor === TypeError // false

บทความที่เกี่ยวข้อง