Nghịch lý trong Constructor
Bản build của bạn bị lỗi. Terminal hiển thị một thông báo khó hiểu về super và this. Bạn chỉ đang cố gắng tái cấu trúc một service cốt lõi, nhưng giờ đây IDE của bạn đầy những đường kẻ đỏ. Lỗi này là một trải nghiệm kinh điển mà lập trình viên TypeScript nào cũng phải trải qua.
'super' must be called before accessing 'this' in the constructor of a derived class.
JavaScript và TypeScript hiện đại tuân thủ một giao thức kế thừa nghiêm ngặt. Lớp cha phải tồn tại trước khi lớp con có thể sửa đổi nó. Nếu bạn cố gắng chạm vào this trước khi gọi super(), engine sẽ chặn bạn vì instance thực tế vẫn chưa được khởi tạo. Về cơ bản, bạn đang cố gắng sơn một căn phòng trong một ngôi nhà thậm chí còn chưa được dựng khung.
Một ví dụ lỗi thực tế
Giả sử bạn đang xây dựng một DatabaseService. Bạn muốn mở rộng một lớp cơ sở để xử lý logic người dùng cụ thể, nhưng bạn lại cố gắng thiết lập một thuộc tính quá sớm:
class BaseService {
protected apiVersion: number;
constructor(version: number) {
this.apiVersion = version;
}
}
class UserService extends BaseService {
private endpoint: string;
constructor(version: number) {
// ❌ Lỗi: truy cập this.endpoint trước khi gọi super()
this.endpoint = '/users';
super(version);
}
}
Việc gán this.endpoint đó sẽ kích hoạt lỗi. TypeScript nhận thấy bạn đang tương tác với instance UserService trước khi BaseService có cơ hội thiết lập trạng thái nội bộ của chính nó.
Tại sao Engine lại chặn bạn
Trong các class ES6, từ khóa this không tự nhiên khả dụng ngay khi constructor bắt đầu. Thay vào đó, lời gọi super() chính là thứ liên kết this với instance hiện tại. Cho đến khi hàm đó hoàn tất, this vẫn chưa được khởi tạo.
Việc cho phép truy cập this trước super() sẽ rất nguy hiểm. Bạn có thể gọi một phương thức phụ thuộc vào thuộc tính của lớp cha vốn đang là undefined. Ràng buộc này ngăn chặn toàn bộ các lỗi crash khi thực thi (runtime) từng gây khó khăn cho các mô hình JavaScript cũ hơn.
Cách sửa trong 30 giây: Thay đổi thứ tự
Hầu hết các trường hợp, giải pháp rất đơn giản. Hãy di chuyển super() lên vị trí đầu tiên trong constructor của bạn. Điều này đảm bảo nền móng vững chắc trước khi bạn bắt đầu trang trí.
class UserService extends BaseService {
private endpoint: string;
constructor(version: number) {
super(version); // ✅ Nền móng đã được đặt
this.endpoint = '/users'; // ✅ Đã an toàn để sử dụng 'this'
}
}
Xử lý logic trước khi gọi Super
Nếu bạn cần tính toán một giá trị để truyền vào lời gọi super() thì sao? Bạn không thể dùng this để làm việc đó, nhưng bạn cũng không bị bế tắc.
Cách làm sai
constructor(config: any) {
this.token = this.decrypt(config.key); // Lỗi: this.decrypt chưa khả dụng
super(this.token);
}
Cách làm đúng: Biến cục bộ và Static Method
Sử dụng các biến cục bộ hoặc hàm bổ trợ static. Những thứ này tồn tại trên chính class chứ không phải instance, vì vậy chúng không cần this để hoạt động.
class UserService extends BaseService {
constructor(config: any) {
// 1. Thực hiện logic bằng các hằng số cục bộ
const decryptedToken = UserService.decrypt(config.key);
// 2. Truyền kết quả cho lớp cha
super(decryptedToken);
// 3. Hoàn tất thiết lập instance
console.log("User service đang hoạt động.");
}
private static decrypt(key: string): string {
return key.split('').reverse().join(''); // Logic đơn giản hóa
}
}
Xác minh và Kiểm thử
Đừng chỉ tin vào việc không còn các đường kẻ đỏ. Hãy xác minh cách sửa của bạn bằng ba bước sau:
- Chạy trình biên dịch: Chạy
npx tsc. Nếu nó không trả về lỗi, chuỗi kế thừa của bạn đã ổn về mặt cú pháp. - Kiểm tra lỗi Runtime: Nếu bạn thấy
ReferenceError: Must call super constructor...trong console trình duyệt, có thể bạn đang có một truy cậpthisẩn trong phần khởi tạo thuộc tính. - Unit Test việc khởi tạo: Tạo một test case để khởi tạo class. Xác nhận rằng cả thuộc tính cha và con (ví dụ:
this.apiVersionvàthis.endpoint) đều đúng như mong đợi.
Tuân thủ quy tắc "super trước tiên" giúp mã nguồn hướng đối tượng của bạn trở nên dễ đoán hơn. Hãy sử dụng các hàm static bổ trợ cho logic tiền khởi tạo và bạn sẽ không bao giờ gặp lại lỗi này nữa.

