Trong các ứng dụng quy mô lớn, việc quản lý State là nơi phát sinh nhiều lỗi nhất. Senior TS Engineer sử dụng Discriminated Unions (hoặc Tagged Unions) để đảm bảo trình biên dịch (Compiler) luôn hiểu rõ trạng thái hiện tại là gì.
1. Vấn đề của cách làm thông thường
Nếu bạn thiết kế một interface chứa tất cả các thuộc tính "có thể có":
interface NetworkState {
status: 'loading' | 'success' | 'error';
data?: string;
error?: string;
}Khi status === 'success', bạn vẫn phải check if (state.data) vì nó là dấu ?. Đây là nguồn gốc của lỗi logic.
2. Giải pháp: Phân biệt bằng "Discriminant"
Chúng ta tách nhỏ interface và sử dụng một thuộc tính chung làm "nhãn" (Tag).
interface LoadingState {
type: 'loading';
}
interface SuccessState {
type: 'success';
data: string;
}
interface ErrorState {
type: 'error';
message: string;
}
type State = LoadingState | SuccessState | ErrorState;3. Lab: Sức mạnh của Exhaustiveness Checking
Hãy chạy bài Lab sau để thấy TS "thông minh" như thế nào khi bạn dùng switch/case.
4. Tại sao đây là Senior Pattern?
- Chống lỗi Runtime: Bạn không thể truy cập
action.usernếutypeđang làLOGOUT. - Tự động cập nhật: Nếu bạn thêm một
typemới vàoActionmà quên xử lý trongswitch/case, TS sẽ báo lỗi ngay lập tức tại dòng_exhaustiveCheck(vìneverkhông thể gán bằng type mới). - Hỗ trợ Refactoring: Khi thay đổi cấu trúc của một trạng thái, TS sẽ chỉ ra chính xác mọi nơi cần sửa chữa trong toàn bộ dự án.
So với cách làm của JavaScript thuần (phải unit test cực kỳ nhiều để đảm bảo an toàn), Discriminated Unions giúp bạn "xây dựng sự an toàn" ngay từ lớp định nghĩa dữ liệu.