Xử lý lỗi trong TypeScript: Ngừng Throwing, bắt đầu Returning
Trong TypeScript, chúng ta thường giả định rằng các hàm luôn trả về giá trị mong đợi. Nhưng điều gì sẽ xảy ra khi có lỗi? Nhiều hàm có thể ném lỗi (throw errors), nhưng TypeScript vẫn gán kiểu dữ liệu như thể chúng luôn thành công. Điều này có thể dẫn đến các lỗi sập ứng dụng không mong muốn và thông tin kiểu dữ liệu sai lệch.
Hãy cùng phân tích vấn đề này với một ví dụ và xem cách neverthrow giải quyết nó — mà không cần khai báo kiểu tường minh!
Vấn đề: Ngoại lệ ẩn (Hidden Exceptions)
Giả sử chúng ta có một hàm lấy tuổi người dùng từ cơ sở dữ liệu. Nếu ID người dùng không hợp lệ, hàm sẽ ném ra lỗi.
function getUserAge(userId: number) {
if (userId < 0) throw new Error("Invalid user ID");
return 25; // Fake age for demo
}
const age = getUserAge(-1); // This might throw an error, yet it's typed as number
console.log("User age:", age);
Tại sao đây là vấn đề?
- Kiểu dữ liệu sai lệch: TypeScript suy luận rằng getUserAge() trả về number, dù thực tế nó có thể ném lỗi.
- Lỗi sập chương trình: Nếu ID không hợp lệ, hàm không thực sự trả về giá trị mà sẽ ném lỗi thay thế.
- Không có kiểm soát lỗi từ kiểu dữ liệu: Không có gì trong chữ ký hàm cho thấy rằng lỗi có thể xảy ra.
Khi bạn di chuột qua getUserAge
, TypeScript sai lầm khi hiển thị kiểu:
function getRandomNumber(): number
Giải pháp: Sử dụng neverthrow
Thay vì ném lỗi, chúng ta có thể sử dụng neverthrow
để trả về lỗi một cách rõ ràng.
import { ok, err } from "neverthrow";
function getUserAge(userId: number) {
if (userId < 0) return err("Invalid user ID"); // Explicit error
return ok(25); // Success case
}
const ageResult = getUserAge(-1);
TypeScript tự động suy ra những gì?
function getRandomNumber(): Err<never, "Invalid user ID"> | Ok<number, never>
Trong đó:
Ok<number, never>
→ Nếu thành công, nó chứa một số (number).Err<never, "Invalid user ID">
→ Nếu thất bại, nó chứa chính xác lỗi "Invalid user ID".
Tại sao cách này tốt hơn?
- Không còn ngoại lệ ẩn: Chương trình không bao giờ ném lỗi bất ngờ nữa.
- Kiểu lỗi chính xác hơn: Thay vì chỉ dùng string hoặc Error, TypeScript tự động suy luận giá trị lỗi cụ thể.
- Xử lý lỗi an toàn & rõ ràng: Vì Result buộc chúng ta phải xử lý cả hai trường hợp, code trở nên an toàn và dễ đọc hơn:
if (ageResult.isErr()) {
console.error("Error:", ageResult.error); // Typed as "Invalid user ID"
} else {
console.log("User age:", ageResult.value); // Typed as number
}
Cải tiến: Xử lý lỗi theo phong cách hàm
Thay vì dùng if, chúng ta có thể chuỗi (chain) kết quả bằng .map()
và .mapErr()
:
ageResult
.map(age => console.log("User age:", age)) // Runs if success
.mapErr(error => console.error("Error:", error)); // Runs if failure
Cách này giúp code gọn gàng và dễ đọc hơn!
Kết luận
Sử dụng neverthrow giúp loại bỏ ngoại lệ ẩn và làm cho code trở nên dễ đoán hơn. Thay vì ném lỗi, chúng ta trả về lỗi một cách rõ ràng, giúp TypeScript suy luận chính xác cả hai trường hợp thành công và thất bại.
✅ Không còn kiểu dữ liệu sai lệch
✅ Không còn lỗi sập ứng dụng bất ngờ
✅ Buộc phải xử lý lỗi đúng cách
Áp dụng cách tiếp cận này giúp ứng dụng của bạn trở nên sạch hơn, an toàn hơn và đáng tin cậy hơn!
Cảm ơn các bạn đã theo dõi!
All rights reserved