Tại sao lỗi này xảy ra
TypeScript về cơ bản là tấm lưới an toàn của bạn. Lỗi cụ thể này xuất hiện khi trình biên dịch không chắc chắn 100% rằng một biến sẽ có giá trị tại thời điểm bạn cố gắng sử dụng nó. Đây là một tính năng cốt lõi của strictNullChecks, được thiết kế để ngăn chặn các lỗi undefined trước khi chúng tiếp cận người dùng.
let discount: number;
const isFlashSale = Math.random() > 0.5;
if (isFlashSale) {
discount = 20;
}
// Lỗi: Variable 'discount' is used before being assigned.
console.log(discount);
Trong ví dụ trên, nếu isFlashSale là false, biến discount sẽ không có giá trị. TypeScript nhận thấy rủi ro này và chặn quá trình biên dịch để ngăn chặn sự cố tiềm ẩn khi thực thi (runtime).
Các nguyên nhân phổ biến
Hành vi này bắt nguồn từ "Phân tích luồng điều khiển" (Control Flow Analysis) của TypeScript. Bạn có thể gặp phải lỗi này trong một vài tình huống cụ thể:
- Câu lệnh điều kiện thiếu: Bạn đã gán giá trị trong khối
ifnhưng quên khốielse. - Khối Try-Catch: Biến được gán bên trong khối
try, nhưng trình biên dịch lo ngại lỗi có thể xảy ra và nhảy sang khốicatchtrước khi việc gán giá trị hoàn tất. - Khởi tạo chậm: Biến được gán bên trong một callback hoặc một hàm lồng nhau có thể sẽ chạy sau đó.
Cách khắc phục
1. Cung cấp giá trị mặc định
Cách sửa đáng tin cậy nhất là khởi tạo biến ngay lập tức. Điều này đảm bảo biến luôn có một giá trị dự phòng hợp lệ, ngay cả khi logic của bạn không thực hiện cập nhật.
// Thay vì 'let role: string;', hãy sử dụng giá trị mặc định
let role: string = "guest";
if (user.isAdmin) {
role = "admin";
}
console.log(role); // An toàn và sạch sẽ
2. Sử dụng Khẳng định gán chắc chắn (!)
Có những lúc bạn nắm rõ tình hình hơn trình biên dịch. Nếu bạn chắc chắn một biến sẽ được gán—chẳng hạn như thông qua một hook của framework hoặc hàm thiết lập—hãy thêm dấu ! sau tên biến khi khai báo. Điều này yêu cầu TypeScript tin tưởng vào quyết định của bạn.
let databaseUrl!: string;
function setup() {
databaseUrl = "mongodb://localhost:27017";
}
setup();
console.log(databaseUrl.length); // Lỗi đã được loại bỏ
Mẹo chuyên nghiệp: Hãy sử dụng cách này một cách tiết kiệm. Nếu bạn phán đoán sai, ứng dụng sẽ ném ra lỗi TypeError khi chạy, điều này làm mất đi mục đích ban đầu của việc sử dụng TypeScript.
3. Khép kín luồng logic
Nếu bạn sử dụng câu lệnh if, hãy đảm bảo mọi nhánh rẽ có thể xảy ra đều thực hiện gán giá trị. Thêm một khối else thường là đủ để đáp ứng yêu cầu của trình biên dịch.
let status: string;
if (response.code === 200) {
status = "Success";
} else {
status = "Error"; // Mọi nhánh rẽ hiện đã được xử lý
}
console.log(status);
4. Tối ưu hóa với toán tử ba ngôi
Nếu logic của bạn đơn giản, đừng khai báo biến rồi mới gán sau. Thay vào đó, hãy sử dụng toán tử ba ngôi để khai báo và gán giá trị cùng lúc. Điều này cho phép bạn sử dụng const, một thực hành tốt hơn cho bộ nhớ và khả năng đọc mã.
// Sạch hơn và ngăn chặn hoàn toàn lỗi
const status = response.code === 200 ? "Success" : "Error";
5. Xử lý kịch bản Try-Catch
Khi làm việc với API, TypeScript giả định rằng khối try có thể thất bại. Để khắc phục, hãy đảm bảo biến cũng được gán trong khối catch hoặc được khởi tạo trước khi try bắt đầu.
let userData: User;
try {
userData = await fetchUser();
} catch (err) {
userData = { id: 0, name: "Anonymous" }; // Gán giá trị dự phòng
}
console.log(userData.name);
Cách kiểm tra kết quả
Kiểm tra tab "Problems" trong IDE của bạn hoặc chạy trình biên dịch thủ công từ terminal:
npx tsc --noEmit
Nếu terminal không trả về lỗi nào, bạn đã vượt qua các bước kiểm tra việc gán giá trị thành công. Bạn cũng nên chạy bộ test để đảm bảo rằng các giá trị mặc định mới không làm thay đổi hành vi mong đợi của ứng dụng.
Thực hành tốt nhất
- Ưu tiên sử dụng const: Đặt mục tiêu sử dụng
constcho 90% các biến của bạn. Nếu bạn không thể dùngconst, đó thường là dấu hiệu cho thấy logic của bạn có thể được đơn giản hóa. - Đừng tắt chế độ strict: Bạn có thể muốn tắt
strictNullCheckstrongtsconfig.jsonđể ẩn lỗi. Đừng làm vậy. Bạn chỉ đang đổi một cảnh báo khi biên dịch lấy một sự cố sập hệ thống vào giữa đêm. - Giữ mọi thứ đơn giản: Nếu logic gán giá trị của một biến kéo dài 50 dòng mã, trình biên dịch có thể sẽ gặp khó khăn. Hãy chia nhỏ logic phức tạp thành các hàm chuyên dụng nhỏ hơn.

