Sửa lỗi TypeScript "Object is possibly 'undefined'" (ts2532)

beginner🔵 TypeScript2026-03-20| TypeScript 4.x / 5.x, Node.js, React, mọi dự án TypeScript

Error Message

Object is possibly 'undefined'. ts(2532)
#typescript#undefined#null-check#optional-chaining

Lỗi gặp phải

Object is possibly 'undefined'. ts(2532)

TypeScript phát hiện một giá trị có thể là undefined — và bạn đang cố sử dụng nó mà không kiểm tra trước. Trình biên dịch chặn điều này một cách có chủ đích. Nếu giá trị đó thực sự là undefined lúc chạy, ứng dụng của bạn sẽ bị crash.

Các nguyên nhân phổ biến:

  • Truy cập phần tử mảng theo chỉ số: arr[0].name
  • Đọc từ tra cứu Map: map.get(key).value
  • Tham số hàm tùy chọn được dùng mà không kiểm tra
  • Truy vấn DOM: document.querySelector('#btn').addEventListener(...)
  • Thuộc tính có kiểu T | undefined

Nguyên nhân gốc rễ

Kiểm tra null nghiêm ngặt (strictNullChecks: true) được bật mặc định khi bạn dùng "strict": true trong tsconfig.json. Khi cờ này hoạt động, undefined trở thành một kiểu riêng biệt. TypeScript sẽ không cho phép bạn xử lý giá trị T | undefined như thể nó luôn là T.

Ví dụ tái hiện tối giản:

const users: User[] = getUsers();
const first = users[0]; // kiểu: User | undefined
console.log(first.name); // ❌ Object is possibly 'undefined'. ts(2532)

Truy cập mảng theo chỉ số trả về T | undefined vì chỉ số 0 có thể không tồn tại — ví dụ khi getUsers() trả về mảng rỗng.

Cách sửa — Nhiều phương án

1. Optional Chaining (?.) — Gọn nhất cho việc đọc

Toán tử ?. ngắt mạch và trả về undefined thay vì ném lỗi. Không cần thêm biến trung gian.

console.log(first?.name); // ✅ trả về undefined nếu first là undefined

// Có thể chain sâu tùy ý
console.log(user?.address?.city);

// Dùng được cả với lời gọi phương thức
user?.save();

Hãy dùng cách này trước tiên khi kết quả undefined là chấp nhận được — ví dụ khi render giao diện mà giá trị thiếu chỉ đơn giản là không hiển thị gì.

2. Nullish Coalescing (??) — Cung cấp giá trị dự phòng

Kết hợp ?? với optional chaining để cung cấp giá trị mặc định khi giá trị bị thiếu:

const name = user?.name ?? 'Anonymous';
const count = map.get(key)?.total ?? 0;

3. Kiểm tra if tường minh (Type Guard)

Khi bạn cần chạy nhiều dòng logic trên giá trị đó, một khối guard sẽ gọn hơn là rải ?. khắp nơi.

const first = users[0];
if (first) {
  console.log(first.name); // ✅ TypeScript thu hẹp kiểu thành User ở đây
  first.activate();
  first.trackVisit();
}

// Early return hoạt động tốt trong hàm
function greet(user: User | undefined) {
  if (!user) return;
  console.log(`Hello, ${user.name}`); // ✅ an toàn
}

4. Non-Null Assertion (!) — Dùng hạn chế

Toán tử ! là lời cam kết với trình biên dịch: "giá trị này không phải undefined — tôi đảm bảo điều đó." TypeScript tin bạn và bỏ qua kiểm tra. Không có bảo vệ nào lúc runtime.

const btn = document.querySelector('#submit')!;
btn.addEventListener('click', handleClick); // ✅ trình biên dịch hài lòng

// Phiên bản inline
console.log(users[0]!.name);

Chỉ dùng cách này khi bạn thực sự chắc chắn — ví dụ như một phần tử DOM bạn vừa render trong useEffect của React component. Rải ! khắp nơi sẽ phá hỏng toàn bộ mục đích của strict null checks.

5. Truy cập chỉ số mảng — Bật noUncheckedIndexedAccess

Gặp lỗi này cụ thể trên arr[i]? TypeScript 4.1 đã thêm noUncheckedIndexedAccess để làm điều này rõ ràng trên toàn bộ dự án:

// tsconfig.json
{
  "compilerOptions": {
    "noUncheckedIndexedAccess": true
  }
}

Khi bật tùy chọn này, arr[0] được định kiểu là T | undefined ở khắp nơi — không còn bất ngờ. Sau đó hãy kiểm tra trước khi dùng:

const first = users[0];
if (first !== undefined) {
  console.log(first.name); // ✅
}

6. Pattern với Map.get()

const cache = new Map();

// Trước
const config = cache.get('main');
config.timeout = 5000; // ❌ possibly undefined

// Sau — dùng guard
const config = cache.get('main');
if (config) {
  config.timeout = 5000; // ✅
}

// Sau — dùng fallback
const config = cache.get('main') ?? defaultConfig;
config.timeout = 5000; // ✅

7. Truy vấn DOM

Lưu ý rằng querySelector trả về Element | null, không phải undefined — nhưng cách sửa vẫn tương tự:

const btn = document.querySelector('#submit');

// Phương án A: non-null assertion (chỉ dùng khi chắc chắn phần tử tồn tại)
btn!.addEventListener('click', handleClick);

// Phương án B: guard (an toàn hơn cho các phần tử có thể không có trong DOM)
if (btn) {
  btn.addEventListener('click', handleClick);
}

Khi nào KHÔNG nên dùng Non-Null Assertion

Một số giá trị thực sự là tùy chọn lúc runtime. Dùng ! trên những giá trị này sẽ đổi lỗi compile-time thành crash runtime — kết quả còn tệ hơn.

  • Phản hồi API: các trường có thể bị thiếu trên phiên bản server cũ hoặc khi có lỗi
  • Dữ liệu nhập từ người dùng và giá trị form
  • Config được tải từ file hoặc biến môi trường
  • Dữ liệu từ thư viện bên thứ ba không có strict typings

Hãy viết guard thực sự hoặc cung cấp giá trị dự phòng cho tất cả những trường hợp này.

Xác minh

Chạy trình biên dịch TypeScript trực tiếp để xác nhận không còn lỗi nào:

# Kiểm tra toàn bộ dự án
npx tsc --noEmit

# Kiểm tra một file cụ thể
npx tsc --noEmit src/utils.ts

Trong VS Code, gạch đỏ bên dưới sẽ biến mất ngay khi bạn lưu file. Di chuột qua biến — nếu TypeScript đã thu hẹp kiểu từ User | undefined thành User, nghĩa là cách sửa đã có tác dụng.

Phòng ngừa

  • Giữ "strict": true trong tsconfig.json. Tắt strictNullChecks để bịt miệng lỗi này không phải là cách sửa
  • Mặc định dùng ?. khi đọc thuộc tính lồng nhau từ nguồn dữ liệu bên ngoài
  • Dùng generic có kiểu cho truy vấn DOM: querySelector<HTMLInputElement>
  • Thêm kiểu trả về tường minh cho các hàm — sẽ dễ nhận ra khi hàm có thể trả về undefined
  • Xác thực phản hồi API tại điểm đầu vào bằng thư viện schema như Zod để các kiểu nội bộ luôn được định nghĩa đầy đủ

Related Error Notes