Fix: JSX element type 'Component' does not have any construct or call signatures (ts2604)

intermediate🔵 TypeScript2026-03-20| TypeScript 4.x/5.x, React 17/18, Node.js 16+, VS Code hoặc IDE hỗ trợ TypeScript

Error Message

JSX element type 'Component' does not have any construct or call signatures. ts(2604)
#typescript#jsx#react#component#ts2604

Chuyện gì đã xảy ra

Bạn đang truyền một thứ gì đó làm JSX component — một biến, một prop, thứ gì đó lấy ra từ một map — và TypeScript từ chối compile. Lỗi trông như thế này:

JSX element type 'Component' does not have any construct or call signatures. ts(2604)

Lỗi này thường xuất hiện trong các tình huống render động: lưu component vào biến, nhận nó qua prop, hoặc lấy nó từ bảng tra cứu. Giá trị đó chạy hoàn toàn bình thường lúc runtime — React xử lý mà không phàn nàn gì. TypeScript đơn giản là không xác định được kiểu của nó, nên chặn quá trình build.

Tại sao TypeScript ném lỗi này

Để JSX hợp lệ, TypeScript phải xác nhận phần tử đó là function component — (props) => JSX.Element — hoặc là class component kế thừa React.Component. Khi kiểu là any, object, unknown, hoặc một interface không liên quan, phép kiểm tra đó thất bại. TypeScript không có cách nào biết giá trị đó có thể render được.

Tái hiện lỗi

Ví dụ đơn giản nhất:

// ❌ Bị lỗi
const Component = someMap[key]; // được suy luận là `object` hoặc `any`
return <Component />; // ts(2604)

Một trường hợp tinh vi hơn — kiểu prop khai báo sai:

// ❌ Kiểu prop sai
interface Props {
  icon: object; // với TypeScript đây chỉ là object thuần, không phải component
}

const Wrapper = ({ icon: Icon }: Props) => {
  return <Icon />; // ts(2604)
};

Cách sửa 1 — Dùng đúng kiểu cho component prop

Khi một prop được thiết kế để nhận component, hãy khai báo kiểu là React.ComponentType hoặc React.ElementType. Không dùng object, không dùng any.

import React from 'react';

interface Props {
  icon: React.ComponentType; // ✅ bao gồm cả function và class component
  // cần truyền props vào nó? Khai báo rõ ràng:
  icon: React.ComponentType<{ size?: number }>;
}

const Wrapper = ({ icon: Icon }: Props) => {
  return <Icon />; // ✅
};

Cần hỗ trợ cả thẻ HTML như "div" hay "span"? Dùng React.ElementType thay thế — kiểu này bao gồm cả component lẫn phần tử gốc:

interface Props {
  as?: React.ElementType; // 'div', 'span', MyComponent — đều hợp lệ
}

const Box = ({ as: Tag = 'div', children }: React.PropsWithChildren<Props>) => {
  return <Tag>{children}</Tag>; // ✅
};

Cách sửa 2 — Khai báo kiểu cho map chứa component

Lấy component từ một map? Hãy khai báo kiểu cho map đó để TypeScript biết mỗi giá trị là một component:

import React from 'react';

const componentMap: Record<string, React.ComponentType> = {
  home: HomeIcon,
  user: UserIcon,
  settings: SettingsIcon,
};

const Component = componentMap[key]; // giờ đã có kiểu React.ComponentType
return <Component />; // ✅

Bị kẹt với một giá trị có kiểu kém từ thư viện bên thứ ba mà bạn không thể sửa? Type assertion là lựa chọn cuối cùng — nhưng hãy biết rằng nó loại bỏ type safety cho biến đó:

const Component = someMap[key] as React.ComponentType;
return <Component />; // ✅ — chỉ dùng khi thực sự cần thiết

Cách sửa 3 — Đổi tên biến sang PascalCase

Cái này hay khiến người ta bất ngờ. JSX xử lý tên biến viết thường như thẻ HTML, không phải component. Dù kiểu có hoàn toàn đúng đi nữa, tên viết thường vẫn gây ra vấn đề:

// ❌ React hiểu đây là thẻ HTML tên "component"
const component = MyComponent;
return <component />;

// ✅ PascalCase báo cho React biết đây là component
const Component = MyComponent;
return <Component />;

Destructuring từ props? Đổi tên ngay tại chỗ khi destructure:

const { icon: Icon } = props;
return <Icon />; // ✅

Cách sửa 4 — Kiểm tra khai báo class component

Đang viết class component? Nó phải kế thừa React.Component. Nếu thiếu, TypeScript sẽ coi nó là class thông thường — không có call signature, không có construct signature, chỉ là một object:

// ❌ Thiếu extends — TypeScript sẽ không coi đây là component
class MyButton {
  render() {
    return <button>Click</button>;
  }
}

// ✅
class MyButton extends React.Component {
  render() {
    return <button>Click</button>;
  }
}

Cách sửa 5 — Kiểm tra tsconfig.json khi toàn bộ JSX bị lỗi

ts(2604) xuất hiện trên mọi phần tử JSX, không chỉ những chỗ dynamic? Đó là dấu hiệu của vấn đề cấu hình chứ không phải kiểu dữ liệu. Kiểm tra trường jsx trong tsconfig.json:

{
  "compilerOptions": {
    "jsx": "react-jsx",  // React 17+ — không cần import thủ công
    // "jsx": "react" cho các dự án cũ hơn
    "lib": ["dom", "esnext"]
  }
}

Xác nhận đã sửa xong

Chạy trực tiếp TypeScript compiler — không có lỗi nào nghĩa là bạn đã ổn:

# Kiểm tra kiểu mà không sinh file output
npx tsc --noEmit

# Dự án Next.js hoặc Vite
npm run build

Trong VS Code, hover vào biến component sau khi sửa. Bạn sẽ thấy React.ComponentType<...>, không phải object hay any. Nếu vẫn thấy kiểu mơ hồ, nghĩa là annotation chưa được áp dụng đúng chỗ.

Tóm tắt nhanh

  • Component truyền qua prop → khai báo kiểu là React.ComponentType hoặc React.ElementType
  • Component lấy từ map → khai báo map là Record<string, React.ComponentType>
  • Tên biến viết thường → đổi sang PascalCase trước khi dùng trong JSX
  • Class component → xác nhận nó có extends React.Component
  • Toàn bộ JSX bị lỗi → kiểm tra trường jsx trong tsconfig.json

Bài học rút ra

ts(2604) là TypeScript đang phát hiện một loại bug thực sự: vô tình render thứ không phải component. Cách sửa gần như không bao giờ là vấn đề logic — mà là annotation kiểu bị thiếu hoặc sai, thường nằm ở prop hoặc giá trị trong map.

Có hai điều đáng ghi nhớ. Thứ nhất, khai báo kiểu cho component prop là React.ComponentType, không phải object. Thứ hai, luôn đổi tên prop component khi destructure sang PascalCase trước khi dùng trong JSX. Tên viết thường trong JSX sẽ âm thầm trở thành thẻ HTML — TypeScript sẽ bắt lỗi, nhưng thông báo lỗi không phải lúc nào cũng nói rõ điều đó.

Related Error Notes