TypeScript narrow theo control flow analysis.
- Với property có thể thay đổi (mutable), compiler chỉ giữ kết quả narrow chừng nào nó còn chắc chắn không ai ghi đè.
- Gọi hàm khác hoặc vào một callback chạy sau là hai điểm nó buộc phải bỏ giả định.
ts
function handle(user: { name?: string }) {
if (user.name) {
doSomething()
user.name.trim() // vẫn OK: property của object không bị reset bởi lời gọi hàm
}
if (user.name) {
setTimeout(() => user.name.trim(), 0) // error: name có thể là undefined
}
}Nguyên nhân: callback có thể chạy ở thời điểm bất kỳ sau đó, trong khoảng đó user.name có thể bị gán lại, nên compiler không giữ narrowing xuyên qua ranh giới hàm.
Cách xử lý:
- Gán vào biến const cục bộ — cách gọn và đúng nhất, vì const không thể đổi:
ts
const name = user.name
if (name) setTimeout(() => name.trim(), 0) // OK- Khai báo property là
readonlykhi nó thực sự bất biến. - Với union rộng, tách sớm ra biến đã narrow rồi truyền xuống, thay vì narrow lại nhiều lần.
Trường hợp tương tự hay gặp: narrow một property rồi truyền cả object vào hàm khác — hàm đó vẫn nhận kiểu gốc, phải narrow lại bên trong.