strictNullChecks: bắt "cannot read property of undefined" lúc compile
strictNullChecks tách null và undefined ra khỏi mọi kiểu khác: khi bật, một string không chứa null trừ khi khai báo rõ string | null. Hệ quả là compiler buộc xử lý trường hợp giá trị có thể vắng trước khi truy cập nó — biến lỗi runtime phổ biến nhất của JavaScript ("Cannot read properties of undefined") thành lỗi compile. Đây là tùy chọn đáng giá nhất trong strict, và việc bật nó trên một codebase có sẵn là một bài tập về xử lý null đúng cách thay vì giấu nó đi.
Cơ chế hoạt động
Khi tắt strictNullChecks, null/undefined gán được cho mọi kiểu, nên compiler không bao giờ cảnh báo về giá trị vắng. Khi bật, chúng là kiểu riêng phải được khai báo và thu hẹp:
function getUser(id: string): User | undefined { /* có thể không tìm thấy */ }
const u = getUser('1')
u.name // lỗi compile: u có thể undefined, phải kiểm tra trước
if (u) u.name // thu hẹp bằng kiểm tra
u?.name // optional chaining: undefined nếu u vắng
const name = u?.name ?? 'khách' // nullish coalescing: default khi null/undefined
?. dừng và trả undefined nếu mắt xích trái vắng (không throw); ?? cung cấp giá trị mặc định chỉ khi trái là null/undefined (khác || vốn cũng thay cả 0/''/false). Đây là bộ công cụ chuẩn để xử lý vắng giá trị an toàn.
Vấn đề gặp trong production
Failure mode mà strict ngăn được: chuỗi truy cập trên dữ liệu có thể vắng. Không strict, đoạn này compile sạch nhưng nổ runtime khi order hoặc customer vắng:
const city = order.customer.address.city // nổ nếu bất kỳ mắt xích nào undefined
Với strict, nếu các field này khai báo optional, compiler buộc xử lý: order?.customer?.address?.city ?? 'unknown'. Lỗi chuyển từ runtime (giữa production, dưới tải) sang compile (lúc viết).
Failure mode: dùng || thay ?? cho giá trị có thể là 0/rỗng. Lỗi tinh vi rất hay gặp:
const qty = input.quantity || 1 // BUG: input.quantity = 0 -> qty thành 1
const qty = input.quantity ?? 1 // đúng: chỉ default khi null/undefined, giữ 0
|| coi 0, '', false là falsy nên thay chúng bằng default — sai khi 0/rỗng là giá trị hợp lệ. ?? chỉ thay null/undefined. Bug này âm thầm vì với phần lớn input nó "có vẻ đúng".
Failure mode: ép kiểu ! (non-null assertion) bừa. u!.name bảo compiler "tôi chắc không null" — nhưng đó là lời hứa không được kiểm tra, nếu sai thì lại nổ runtime đúng như khi chưa strict. ! chỉ nên dùng khi thật sự chắc chắn về bất biến mà compiler không thấy được, không dùng để "tắt" cảnh báo cho nhanh.
Cách debug và monitor
Khi bật strict trên codebase có sẵn, số lỗi compile ban đầu lớn — đó là danh sách các chỗ vốn đã có nguy cơ null, không phải lỗi mới. Xử lý từng cái bằng thu hẹp đúng (if, ?., ??), tránh giải quyết lười bằng ! hàng loạt (chỉ dời lỗi sang runtime). Đếm số ! trong codebase như chỉ số rủi ro — mỗi cái là một lời hứa chưa kiểm. Khi một runtime "undefined" vẫn lọt qua dù đã strict, gần như luôn có một as/! hoặc một any ở ranh giới dữ liệu ngoài đã che mất khả năng vắng. Bật cùng strictNullChecks các rule liên quan để công cụ nhắc dùng ??/?. đúng chỗ.
Tradeoff
Strict null check tăng an toàn đáng kể — bắt cả lớp lỗi null/undefined lúc compile, ép xử lý trường hợp vắng giá trị một cách tường minh thay vì để runtime phát hiện. Cái giá là nhiều cảnh báo hơn (nhất là khi bật trên code cũ) và phải viết xử lý null rõ ràng ở những chỗ trước đây bỏ qua. Đó là sự đánh đổi gần như luôn đáng: công viết thêm lúc compile rẻ hơn nhiều so với một crash production. Quy tắc thực tế: bật strict ngay từ đầu dự án; xử lý vắng giá trị bằng thu hẹp/?./?? chứ không bằng !; và dùng ?? (không ||) khi 0/''/false là giá trị hợp lệ.
Câu hỏi phỏng vấn
Có nên bật strict (strictNullChecks) không, và
??khác||ở đâu?
Nên bật — gần như luôn. strictNullChecks tách null/undefined khỏi các kiểu khác, buộc xử lý trường hợp giá trị vắng trước khi truy cập, nên chuyển lỗi "Cannot read properties of undefined" — lỗi runtime phổ biến nhất của JS — thành lỗi compile bắt được lúc viết. Cái giá là nhiều cảnh báo hơn và phải viết thu hẹp tường minh, nhưng rẻ hơn nhiều một crash production. ?? (nullish coalescing) chỉ thay giá trị khi bên trái là null/undefined, còn || thay với mọi giá trị falsy gồm 0, '', false — nên input.quantity || 1 biến 0 hợp lệ thành 1 (bug), trong khi input.quantity ?? 1 giữ đúng 0. Điểm ăn điểm: tránh lạm dụng non-null assertion ! vì nó chỉ dời lỗi sang runtime, và cảnh giác any/as ở ranh giới dữ liệu ngoài che mất khả năng vắng.
Hands-on
Lấy một module thật truy cập dữ liệu lồng sâu có thể vắng (ví dụ order.customer.address.city từ một API), tắt strictNullChecks để thấy nó compile sạch rồi nổ runtime khi một mắt xích undefined. Bật strict, xử lý các lỗi compile mới hiện bằng optional chaining và nullish coalescing thay vì rải !. Tạo riêng một bug || với một field có giá trị hợp lệ là 0 (số lượng, giá giảm), quan sát giá trị bị thay sai, rồi đổi sang ?? và xác nhận 0 được giữ. Cuối cùng đếm số !/as còn lại trong module và thay chúng bằng thu hẹp kiểu đúng.
Top comments (0)