ใน TypeScript การจัดการข้อผิดพลาด (หรือที่เรียกว่า Errors) เป็นกลไกที่ช่วยให้โปรแกรมทำงานต่อไปได้ แทนที่จะหยุดทำงานทันทีเมื่อเกิดปัญหาที่ไม่คาดคิด
1. การกำหนดข้อผิดพลาดด้วย throw
ใน TypeScript การสร้างและส่งข้อผิดพลาดออกไปใช้คีย์เวิร์ด throw โดยข้อปฏิบัติที่ดีที่สุดคือการส่ง Object ที่สืบทอดมาจากคลาส Error
A. การสร้าง Custom Error
TS แนะนำให้สร้างคลาสข้อผิดพลาดเฉพาะเจาะจงโดยการ extends Error (เทียบเท่ากับการ implements Exception ใน Dart แต่เป็นมาตรฐานของ JS/TS)
// Custom Error Class
class WithdrawError extends Error {
constructor(message?: string) {
super(message); // เรียก constructor ของ Error class
this.name = 'WithdrawError'; // กำหนดชื่อ Error (สำคัญในการระบุชนิด)
// Object.setPrototypeOf(this, WithdrawError.prototype); // สำหรับ JS เก่า
}
}
// การโยนข้อผิดพลาด (Throwing)
function checkWithdraw(deposit: number, withdraw: number): void {
if (withdraw > deposit) {
// โยน Custom Error
throw new WithdrawError('ยอดเงินไม่เพียงพอ');
}
if (withdraw <= 0) {
// โยน Base Error
throw new Error('ยอดเงินที่ต้องการถอนต้องมากกว่า 0');
}
}
ข้อควรจำ: ใน TS/JS สามารถ
throwค่าพื้นฐาน (เช่นthrow "String") ได้ แต่ ไม่ควรทำ เพราะจะทำให้การตรวจสอบชนิดของข้อผิดพลาดทำได้ยาก
2. การตรวจจับและจัดการด้วย try...catch
เราใช้บล็อก try เพื่อครอบโค้ดที่อาจเกิดข้อผิดพลาด และใช้ catch เพื่อจัดการข้อผิดพลาดที่ถูกโยนมา
A. การใช้งาน try...catch พื้นฐาน
function runTransaction() {
try {
checkWithdraw(200, 300); // โค้ดที่อาจเกิดข้อผิดพลาด
} catch (e) {
// e คือ error object ที่ถูก throw ออกมา
console.error("Transaction failed:", e.message);
// Output: Transaction failed: ยอดเงินไม่เพียงพอ
}
}
B. การระบุชนิดข้อผิดพลาด (ใช้ instanceof แทน Dart on)
TypeScript ไม่มีคีย์เวิร์ด on เหมือน Dart ในการระบุชนิดของ Exception โดยตรงใน catch block แต่เราใช้วิธีการตรวจสอบชนิดของ Error Object ด้วย instanceof แทน
function runSpecificCatch() {
try {
checkWithdraw(200, 300);
} catch (error) {
// 1. ตรวจสอบชนิด Custom Error ก่อน
if (error instanceof WithdrawError) {
console.warn(`[Withdraw Check] - ${error.message}`);
}
// 2. ตรวจสอบชนิด Base Error
else if (error instanceof Error) {
console.error(`[Base Error] - ${error.message}`);
}
// 3. จัดการกรณีที่ throw มาเป็น String หรือค่าอื่น ๆ (ซึ่งไม่แนะนำ)
else {
console.error("Unknown Error type:", error);
}
}
}
C. การเข้าถึง Stack Trace
Stack Trace ซึ่งแสดงลำดับการเรียกฟังก์ชันที่ทำให้เกิดข้อผิดพลาด จะเป็น Property หนึ่งของ Object ที่สืบทอดจาก Error
try {
throw new Error("Detailed failure.");
} catch (error) {
if (error instanceof Error) {
console.log(`Error Name: ${error.name}`);
console.log(`Stack Trace:n ${error.stack}`); // เข้าถึง Stack Trace
}
}
3. การใช้งาน rethrow (ส่งต่อข้อผิดพลาด)
ใน TypeScript เราไม่มีคีย์เวิร์ด rethrow โดยตรง แต่ใช้คีย์เวิร์ด throw ภายในบล็อก catch เพื่อโยนข้อผิดพลาดเดิมออกไปให้ Handler ที่อยู่สูงกว่า (Outer Handler) จัดการต่อ
function handleAndRethrow(deposit: number, withdraw: number) {
try {
checkWithdraw(deposit, withdraw);
} catch (e) {
if (e instanceof WithdrawError) {
// ทำการบันทึกข้อผิดพลาดในระดับนี้ (Log)
console.warn(`[Local Handler] Logged withdrawal error for user.`);
// แล้วส่งต่อข้อผิดพลาดนี้ไปให้ Handler ภายนอก
throw e;
}
// Error อื่น ๆ จะถูกส่งต่อโดยอัตโนมัติหากไม่ถูกจับ
}
}
try {
handleAndRethrow(200, 300);
} catch (e) {
// Handler ภายนอกจะได้รับ WithdrawError ต่อจาก handleAndRethrow
console.error(`[Global Handler] Transaction failed completely.`);
}
4. การใช้งาน finally
บล็อก finally จะทำงานเสมอ ไม่ว่าในบล็อก try จะเกิดข้อผิดพลาดหรือไม่ หรือเกิดแล้วถูกจัดการด้วย catch หรือไม่ก็ตาม มักใช้สำหรับโค้ดทำความสะอาด (Cleanup) เช่น การปิดไฟล์ หรือปิดการเชื่อมต่อฐานข้อมูล
function runWithCleanup() {
let connectionStatus = 'open';
try {
// โค้ดที่อาจเกิด error
console.log('Attempting operation...');
// throw new Error("Connection lost");
} catch (e) {
console.error("Operation failed.");
} finally {
// โค้ดใน finally จะทำงานเสมอ
connectionStatus = 'closed';
console.log(`Connection is now: ${connectionStatus}`);
}
}
runWithCleanup();