Cách khắc phục lỗi TypeScript: Cannot assign to 'property' because it is a read-only property

beginner🔵 TypeScript2026-04-06| TypeScript 3.0+, VS Code, môi trường Node.js

Error Message

Cannot assign to 'property' because it is a read-only property.
#typescript#frontend#phat-trien-web#meo-lap-trinh

TL;DR: Các cách sửa nhanh

Bạn đang gấp? Hãy sử dụng một trong ba phương pháp sau để code chạy được ngay lập tức:

  • Chỉnh sửa mã nguồn: Tìm interface hoặc type và xóa từ khóa readonly.
  • Ép buộc thay đổi: Sử dụng type assertion để bỏ qua kiểm tra: (myObject as any).property = 'giá trị mới';.
  • Bỏ const: Nếu bạn định nghĩa đối tượng với as const, hãy xóa hậu tố đó để đối tượng có thể thay đổi (mutable) trở lại.

Tại sao TypeScript ngăn chặn việc chỉnh sửa

Bạn sẽ gặp lỗi này khi trình biên dịch phát hiện nỗ lực thay đổi một giá trị đã được đánh dấu rõ ràng là bất biến (immutable). TypeScript làm điều này để ngăn chặn các lỗi do thay đổi trạng thái không mong muốn. Thông thường, nguyên nhân nằm ở một trong ba trường hợp sau:

  • Thuộc tính trong interface hoặc type có tiền tố readonly.
  • Đối tượng được khai báo với Const Assertion (as const), khiến mọi trường dữ liệu đều không thể thay đổi.
  • Bạn đang cố gắng cập nhật một thuộc tính class được đánh dấu readonly bên ngoài constructor.
interface User {
  readonly id: number;
  name: string;
}

const user: User = { id: 101, name: 'Alice' };
// ❌ Lỗi: Cannot assign to 'id' because it is a read-only property.
user.id = 202; 

4 cách để giải quyết lỗi

1. Chỉnh sửa định nghĩa Interface

Cách đơn giản nhất là cho phép thuộc tính thay đổi. Nếu logic của bạn yêu cầu cập nhật giá trị, như retryCount hoặc timestamp lastLogin, thì flag readonly ngay từ đầu không nên xuất hiện ở đó.

// Trước: Bất biến
interface AppConfig {
  readonly port: number;
}

// Sau: Có thể thay đổi
interface AppConfig {
  port: number;
}

2. Sử dụng Type Assertion (Lối thoát hiểm)

Bạn có thể gặp lỗi này khi sử dụng các thư viện bên thứ ba mà bạn không thể chỉnh sửa mã nguồn. Trong những trường hợp này, bạn có thể bảo TypeScript "hãy tin tưởng bạn" bằng cách ép kiểu đối tượng. Điều này thường thấy trong unit test khi bạn cần giả lập (mock) các trạng thái dữ liệu cụ thể.

interface Profile {
  readonly email: string;
}

const userProfile: Profile = { email: 'test@example.com' };

// Tùy chọn A: Ép kiểu sang any
(userProfile as any).email = 'new@example.com';

// Tùy chọn B: Tạo một utility type Writable
type Writable<T> = { -readonly [P in keyof T]: T[P] };
(userProfile as Writable<Profile>).email = 'admin@internal.com';

3. Loại bỏ "as const" Assertion

Sử dụng as const là một cách tiện lợi để tạo các literal type, nhưng nó sẽ khóa mọi thuộc tính lồng nhau. Nếu bạn cần thay đổi cài đặt theme từ 'dark' sang 'light', đừng sử dụng const assertion cho toàn bộ đối tượng.

// Đối tượng này bị khóa 100%
const UI_SETTINGS = {
  theme: 'dark',
  sidebarWidth: 250
} as const;

// UI_SETTINGS.theme = 'light'; // ❌ Lệnh này sẽ thất bại

// Cách sửa: Sử dụng một đối tượng tiêu chuẩn hoặc một type cụ thể
const UI_SETTINGS = {
  theme: 'dark',
  sidebarWidth: 250
};

4. Chuyển đổi mảng Readonly

Các phương thức như push(), pop(), hoặc splice() sẽ kích hoạt lỗi này nếu mảng được định nghĩa là readonly string[]. Vì các phương thức này làm thay đổi mảng gốc, TypeScript sẽ chặn chúng. Để khắc phục, hãy tạo một bản sao có thể thay đổi bằng cách sử dụng spread operator.

const roles: readonly string[] = ['admin', 'editor'];

// roles.push('viewer'); // ❌ Lỗi

// Cách sửa: Spread vào một mảng mới
const activeRoles = [...roles];
activeRoles.push('viewer'); // ✅ Hoạt động hoàn hảo

Cách kiểm tra lại công việc của bạn

Sau khi áp dụng bản sửa lỗi, hãy xác minh bằng ba bước sau:

  • Tìm dấu gạch chân đỏ: VS Code sẽ ngay lập tức xóa dấu gạch chân đỏ lượn sóng.
  • Chạy build: Mở terminal và chạy npx tsc. Nếu quá trình kết thúc không có lỗi, bản sửa của bạn là an toàn về kiểu (type-safe).
  • Xác minh JS: Nếu bạn đã sử dụng as any, hãy nhớ rằng TypeScript sẽ không giúp ích gì cho bạn khi chạy (runtime). Hãy kiểm tra log hoặc console trình duyệt để đảm bảo giá trị thực sự đã được cập nhật.

Mẹo chuyên nghiệp: Giữ tính bất biến khi có thể

Đừng chỉ xóa readonly để làm lỗi biến mất. Tính bất biến (immutability) giúp code của bạn dễ debug hơn. Nếu bạn đang làm việc trong React hoặc Redux, thay vì ép buộc thay đổi một thuộc tính read-only, hãy luôn trả về một đối tượng mới: const updated = { ...oldObject, status: 'active' };. Điều này giúp luồng dữ liệu của bạn có thể dự đoán được và tránh các tác dụng phụ (side effects).

Related Error Notes