Tình huống lỗi
Lỗi này thường xảy ra khi bạn cố gắng ép một biến sang một kiểu dữ liệu mà nó không thể thuộc về. TypeScript kiểm tra sự 'chồng lấp' (overlap)—nghĩa là liệu hai kiểu này có bao giờ có điểm chung không? Nếu câu trả lời là 'không,' trình biên dịch sẽ chặn việc ép kiểu để bảo vệ bạn khỏi những lỗi runtime nghiêm trọng.
Hãy xem xét một sai lầm phổ biến khi lập trình viên cố gắng ép kiểu một chuỗi từ API trực tiếp sang số:
const priceInput = "49.99";
// Điều này sẽ gây ra lỗi:
// Conversion of type 'string' to type 'number' may be a mistake...
const price = priceInput as number;
Phân tích: Tại sao TypeScript lại chặn bạn
TypeScript chỉ cho phép ép kiểu (từ khóa as) nếu kiểu này là kiểu con của kiểu kia. Hãy tưởng tượng nó như một cây gia phả. Bạn có thể ép một HTMLElement chung sang một HTMLButtonElement cụ thể vì nút bấm là một loại phần tử. Chúng có chung các thuộc tính.
Các kiểu dữ liệu nguyên thủy như string và number thì khác. Chúng nằm trên những nhánh hoàn toàn riêng biệt. Một chuỗi như "123" không bao giờ thực sự là con số 123 trong mắt trình biên dịch. Ép kiểu as number cho một chuỗi thực chất là "nói dối" runtime. Điều này dẫn đến kết quả NaN hoặc làm ứng dụng bị crash khi thực hiện các phép toán.
Cách sửa nhanh: Ép kiểu kép (Double Casting)
Đôi khi bạn phải làm việc với dữ liệu lộn xộn từ bên thứ ba và bạn chắc chắn 100% về kết quả. Trong những trường hợp hiếm hoi đó, bạn có thể bỏ qua bước kiểm tra an toàn bằng cách sử dụng một kiểu trung gian là unknown. Thông báo lỗi thậm chí còn gợi ý giải pháp này.
const priceInput = "49.99";
// Bước 1: Ép kiểu sang unknown (kiểu dữ liệu cao nhất)
// Bước 2: Ép kiểu sang kiểu dữ liệu đích
const price = (priceInput as unknown) as number;
Lưu ý: Cách này giúp xóa dấu gạch dưới màu đỏ, nhưng nó không làm thay đổi dữ liệu. Ở runtime, price vẫn là chuỗi "49.99". Nếu bạn gọi price.toFixed(2), ứng dụng của bạn sẽ bị crash vì toFixed là phương thức của kiểu số, không phải kiểu chuỗi.
Cách sửa triệt để: Chuyển đổi dữ liệu thực tế
Phần lớn thời gian, thứ bạn cần không phải là ép kiểu (cast) mà là chuyển đổi (conversion). Ép kiểu là yêu cầu trình biên dịch "nhắm mắt làm ngơ". Chuyển đổi thực sự thay đổi giá trị bên dưới sang định dạng chính xác.
Chuyển đổi Chuỗi sang Số
Hãy sử dụng các phương thức JavaScript tiêu chuẩn. Chúng an toàn và dễ đọc hơn:
const priceInput = "49.99";
// Lựa chọn A: Hàm khởi tạo Number (Tốt nhất để chuyển đổi nghiêm ngặt)
const price = Number(priceInput);
// Lựa chọn B: parseFloat (Tuyệt vời để trích xuất số thập phân từ chuỗi lộn xộn)
const priceFloat = parseFloat(priceInput);
Sửa lỗi không tương thích giữa các Đối tượng
Các hàm ánh xạ (mapping functions) là "người bạn thân" nhất khi xử lý các interface không khớp nhau. Nếu API trả về ID kiểu chuỗi nhưng giao diện của bạn cần kiểu số, đừng cố ép kiểu. Hãy ánh xạ nó:
interface UserResponse {
user_id: string; // ví dụ: "101"
user_name: string;
}
interface AppUser {
id: number; // ví dụ: 101
name: string;
}
const rawData = { user_id: "101", user_name: "Alice" } as UserResponse;
// Ánh xạ dữ liệu thủ công để đảm bảo các kiểu dữ liệu khớp nhau
const user: AppUser = {
id: parseInt(rawData.user_id, 10),
name: rawData.user_name
};
Xử lý đối tượng "không đầy đủ" trong Unit Test
Lỗi này thường xuất hiện khi viết unit test. Bạn có thể chỉ muốn mock một vài trường của một interface khổng lồ. Nếu bạn cung cấp một đối tượng quá nhỏ, TypeScript sẽ phàn nàn rằng các kiểu dữ liệu không chồng lấp lên nhau.
Sử dụng Partial<T> thường là cách sửa sạch sẽ nhất. Nó báo cho TypeScript biết rằng bạn chỉ định cung cấp một số thuộc tính, giúp các kiểu dữ liệu 'chồng lấp' trở lại mà không cần dùng đến việc ép kiểu không an toàn.
Danh sách kiểm tra xác minh
Trước khi triển khai code, hãy xác minh bản sửa lỗi bằng ba bước sau:
- Xác minh kiểu dữ liệu lúc Runtime: Thêm
console.log(typeof price). Nó phải in ra"number", chứ không phải"string". - Thử một phép toán: Cộng thêm 10 vào kết quả. Nếu
"49.99" + 10bằng"49.9910", việc ép kiểu của bạn 'có vẻ chạy' nhưng logic đã sai. Nếu nó bằng59.99, bạn đã thành công. - Kiểm tra trình biên dịch: Chạy
tsc --noEmittừ terminal của bạn. Đảm bảo lỗi đã biến mất hoàn toàn.

