Sửa lỗi TypeScript: This expression is not callable. Type 'X' has no call signatures.

beginner🔵 TypeScript2026-04-25| TypeScript 3.0+, VS Code, Node.js, React, hoặc bất kỳ môi trường TS hiện đại nào.

Error Message

This expression is not callable. Type 'X' has no call signatures.
#typescript#phat-trien-web#react#meo-lap-trinh#debugging

Tình huống lỗiBạn đang tập trung viết code, chuẩn bị gọi một hàm thì bỗng nhiên VS Code hiện cảnh báo đỏ. Bạn cố gắng thực thi một biến, nhưng trình biên dịch chặn bạn lại với một thông báo gây ức chế:

This expression is not callable. Type 'UserProfile' has no call signatures.

Điều này xảy ra vì TypeScript là một "người gác cổng" nghiêm ngặt. Nếu bạn không khai báo rõ ràng rằng một đối tượng là một hàm, nó sẽ không cho phép bạn sử dụng dấu ngoặc đơn để gọi đối tượng đó. Bạn sẽ thường thấy lỗi này khi vô tình coi một đối tượng như một hàm, hoặc khi gặp sai sót lúc destructuring một React hook như useState.

Ví dụ về lỗi:```

interface UserProfile { name: string; age: number; }

const user: UserProfile = { name: 'Alex', age: 30 };

// Lỗi: TypeScript hiểu 'user' là một đối tượng, không phải là một hàm. user();


## Tại sao TypeScript lại chặn điều này?Về bản chất, TypeScript tìm kiếm một **call signature** (chữ ký cuộc gọi). Hãy coi đây như một bản hợp đồng. Nó thông báo cho trình biên dịch rằng: "Đối tượng này không chỉ là dữ liệu; nó còn có thể được gọi với các đối số cụ thể này và trả về kiểu dữ liệu cụ thể kia."
Khi lỗi này xuất hiện, thường là do ba sai lầm phổ biến sau:
- **Nhầm lẫn biến:** Bạn đặt tên một đối tượng dữ liệu và một hàm gần giống nhau, và bạn gọi nhầm đối tượng đó.- **Thiếu sót trong Interface:** Bạn thiết kế một đối tượng để có thể gọi được (như một dạng lai giữa hàm và đối tượng) nhưng quên định nghĩa hành vi đó trong kiểu dữ liệu.- **Kiểu dữ liệu không chắc chắn:** Bạn đang làm việc với một union type (ví dụ: `string | (() => void)`) và chưa chứng minh được với TypeScript rằng giá trị hiện tại chắc chắn là một hàm.## Các giải pháp "tạm thời"Đôi khi bạn chỉ cần code chạy ngay lập tức. Mặc dù đây không phải là giải pháp lâu dài cho một codebase sạch, nhưng chúng giúp bạn hoàn thành công việc nhanh chóng.
### 1. Type Assertion (Sử dụng từ khóa 'as')Bạn có thể ghi đè kiểm tra an toàn của trình biên dịch bằng cách ép kiểu. Đây là hành động kiểu "tin tôi đi, tôi biết mình đang làm gì". Hãy sử dụng nó một cách tiết kiệm.

// Ép kiểu về 'any' (Nguy hiểm) (user as any)();

// Tốt hơn: Ép kiểu về một hàm chung (user as Function)();


### 2. Kiểm tra lỗi đánh máy phương thứcNghe có vẻ đơn giản, nhưng hãy kiểm tra lại tên thuộc tính của bạn. Có phải bạn đang cố gọi `api()` trong khi thực tế bạn muốn gọi `api.fetch()` không? Lỗi này chiếm một phần lớn trong các tệp tin dài hơn 500 dòng, nơi các tên biến bắt đầu trông giống hệt nhau.
## Các giải pháp chuyên nghiệp### 1. Thêm Call Signature vào một InterfaceTrong JavaScript, hàm cũng là đối tượng. Bạn có thể có một tiện ích muốn gọi trực tiếp, nhưng nó cũng đính kèm các siêu dữ liệu (metadata). Để làm được điều này trong TypeScript, hãy định nghĩa call signature trực tiếp bên trong interface của bạn.

interface Logger { (message: string): void; // Dòng mã quan trọng: call signature logCount: number; }

const customLogger: Logger = (msg: string) => { console.log(msg); customLogger.logCount++; };

customLogger.logCount = 0; customLogger("Hệ thống đã khởi tạo"); // Bây giờ mã đã hoạt động hoàn hảo


### 2. Sửa lỗi Destructuring React HookĐây là lý do hàng đầu khiến các lập trình viên React gặp phải lỗi này. Nếu bạn sử dụng dấu ngoặc nhọn `{}` thay vì dấu ngoặc vuông `[]` khi gọi `useState`, bạn đang destructuring state sai cách.

// SAI: Destructuring như một đối tượng const { count, setCount } = useState(0); count(); // Lỗi: Kiểu 'number' không thể gọi được.

// ĐÚNG: Destructuring như một mảng const [count, setCount] = useState(0); setCount(prev => prev + 1); // Hoạt động tốt!


### 3. Thu hẹp Union Types (Narrowing)Nếu một biến có thể là `string` hoặc `Function`, TypeScript sẽ không cho phép bạn gọi nó vì lo ngại bạn sẽ thực thi một chuỗi. Hãy sử dụng `typeof` guard để làm hài lòng trình biên dịch.

type Task = string | (() => void);

function processTask(task: Task) { if (typeof task === 'function') { task(); // TS chấp nhận vì chúng ta đã chứng minh nó là một hàm } else { console.log("Tên nhiệm vụ:", task); } }


## Cách xác minh giải pháp của bạnĐừng vội giả định rằng lỗi đã được khắc phục chỉ vì vạch đỏ biến mất. Hãy thực hiện ba bước sau để chắc chắn:
- **Kiểm tra khi di chuột (Hover):** Di chuột qua biến trong IDE của bạn. Bạn sẽ thấy một signature như `(args: any) => void`. Nếu bạn thấy `Type X`, nó vẫn chỉ là một đối tượng.- **Kiểm tra bằng CLI:** Chạy lệnh `npx tsc --noEmit`. Lệnh này thực hiện kiểm tra toàn bộ trình biên dịch mà không tạo tệp đầu ra. Đây là cách kiểm chứng cuối cùng cho các lỗi TS.- **Unit Test:** Nếu bạn đã sử dụng type assertion (`as any`), hãy viết một bài kiểm tra nhanh để đảm bảo biến đó thực sự là một hàm khi chạy (runtime). TypeScript sẽ không cứu được bạn nếu bạn ép một kiểu dữ liệu sai.

Related Error Notes