Cách khắc phục lỗi 'Property has no initializer' trong TypeScript

beginner🔵 TypeScript2026-07-05| TypeScript 2.7+, Node.js, VS Code, hoặc bất kỳ môi trường nào sử dụng chế độ strict trong tsconfig.json.

Error Message

Property 'name' has no initializer and is not definitely assigned in the constructor.
#typescript#web-development#meo-lap-trinh#tsconfig

Tại sao TypeScript lại báo lỗi?Bạn đang xây dựng một class, định nghĩa một thuộc tính, và đột nhiên VS Code xuất hiện gạch chân màu đỏ. Lỗi này xảy ra vì TypeScript lo ngại bạn sẽ cố gắng đọc một thuộc tính trước khi nó thực sự tồn tại. Theo mặc định, nó muốn đảm bảo mọi thuộc tính đều có giá trị hợp lệ trước khi instance của class được khởi tạo hoàn toàn.

Property 'name' has no initializer and is not definitely assigned in the constructor.

Nguyên nhân gốc rễHành vi này được điều khiển bởi flag strictPropertyInitialization. Khi thuộc tính này được đặt thành true trong tsconfig.json của bạn, TypeScript sẽ thực hiện kiểm tra an toàn: mọi thuộc tính class không bắt buộc phải được gán giá trị ngay trên dòng khai báo hoặc bên trong constructor. Điều này giúp ngăn chặn lỗi TypeError: Cannot read property of undefined đáng sợ khi ứng dụng đang chạy.

Lưu ý: Quy tắc này chỉ hoạt động nếu strictNullChecks cũng được bật, vốn là tiêu chuẩn cho các dự án hiện đại.

Cách 1: Phương pháp "Thiết lập và Bỏ qua"Cách khắc phục dễ nhất là cung cấp một giá trị mặc định ngay lập tức. Hãy sử dụng cách này nếu thuộc tính có một điểm bắt đầu hợp lý, chẳng hạn như một chuỗi rỗng hoặc số 0.

class User {
  role: string = "guest"; // Được khởi tạo ngay lập tức
}

Cách 2: Gán giá trị động qua ConstructorNếu thuộc tính của bạn phụ thuộc vào dữ liệu được truyền từ bên ngoài, hãy xử lý nó trong constructor. Khả năng phân tích tĩnh của TypeScript đủ thông minh để nhận thấy rằng giá trị chắc chắn sẽ được thiết lập trong quá trình khởi tạo.

class User {
  name: string;

  constructor(userName: string) {
    this.name = userName; // TypeScript không còn báo lỗi nữa
  }
}

Cách 3: Toán tử "Hãy tin tôi" (!)Đôi khi bạn biết chắc chắn một thuộc tính sẽ được gán bởi một tác nhân bên ngoài—như @Input() trong Angular, một entity TypeORM, hoặc một phương thức init()—nhưng TypeScript không thể nhận biết được điều đó. Toán tử khẳng định gán chắc chắn (Definite Assignment Assertion - !) sẽ yêu cầu trình biên dịch ngừng lo lắng.

class Profile {
  // Dấu "!" nói với TS: "Tôi hứa giá trị này sẽ được gán trước khi tôi sử dụng nó"
  @Input() username!: string;
}

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 quên gán giá trị thực tế, bạn sẽ mất đi các lợi ích về an toàn kiểu dữ liệu mà TypeScript cung cấp.

Cách 4: Chuyển thành Optional hoặc NullableNếu một thuộc tính không bắt buộc đối với mọi instance, hãy đánh dấu nó là optional bằng dấu ?. Ngoài ra, nếu nó bắt đầu bằng trạng thái "không có gì", hãy sử dụng kiểu union với null.

class User {
  bio?: string; // Có thể là string hoặc undefined
  lastLogin: Date | null = null; // Bắt đầu rõ ràng là null
}

Cách 5: Điều chỉnh độ khắt khe của trình biên dịchNếu bạn đang di chuyển một codebase cũ khổng lồ và các lỗi hiện ra quá nhiều, bạn có thể tắt tính năng kiểm tra này. Tuy nhiên, đây thường là một bước lùi về mặt chất lượng mã nguồn.

Trong file tsconfig.json của bạn:

{
  "compilerOptions": {
    "strictPropertyInitialization": false
  }
}

Xác minh: Cách xác nhận lỗi đã được sửaĐừng chỉ tin vào IDE. Hãy xác minh các thay đổi của bạn bằng ba bước sau:

  • Kiểm tra dấu gạch chân đỏ: Đảm bảo rằng phần bôi đỏ lỗi trong VS Code đã biến mất.- Chạy Build toàn bộ: Chạy lệnh npx tsc trong terminal của bạn. Nếu quá trình kết thúc mà không có đầu ra (output) nào, các kiểu dữ liệu của bạn đã hợp lệ.- Kiểm tra logic Runtime: Nếu bạn đã sử dụng toán tử !, hãy xác minh rằng logic khởi tạo của bạn (như gọi API hoặc framework hook) thực sự chạy trước khi thuộc tính được truy cập.## Thực hành tốt nhất: Parameter PropertiesĐể viết mã sạch hơn, hãy sử dụng cách viết tắt constructor của TypeScript. Thay vì khai báo một thuộc tính rồi mới gán giá trị cho nó, hãy thực hiện cả hai cùng lúc:
class User {
  // Dòng duy nhất này vừa khai báo, tiếp nhận và gán giá trị cho 'name'
  constructor(public name: string) {}
}

Mô hình này ngắn gọn hơn gấp 3 lần và hoàn toàn tránh được lỗi 'no initializer' ngay từ khâu thiết kế.

Related Error Notes