Cách sửa lỗi TypeScript: Namespace 'X' has no exported member 'Y'

beginner🔵 TypeScript2026-05-29| Các dự án TypeScript 3.x/4.x/5.x, Node.js, React, hoặc Angular trên Windows, macOS, hoặc Linux.

Error Message

Namespace 'X' has no exported member 'Y'.
#typescript#namespace#declaration-file#d.ts#export

Vấn đề

Gặp phải lỗi "Namespace has no exported member" là một trải nghiệm quen thuộc đối với các lập trình viên TypeScript. Có những lúc các type của bạn đang hoạt động hoàn hảo; nhưng ngay sau đó, một đường kẻ đỏ gợn sóng xuất hiện trong VS Code và quá trình build của bạn thất bại. Điều này thường xảy ra khi bạn cố gắng truy cập một type hoặc interface mà bạn chắc chắn đã định nghĩa, nhưng trình biên dịch lại coi như nó không tồn tại.

// Ví dụ kết quả lỗi
error TS2694: Namespace 'MyProject' has no exported member 'UserConfig'.

Phân tích nguyên nhân gốc rễ

Trình biên dịch của bạn không tự nhiên gây khó dễ. Thông thường, sự mất kết nối này xảy ra vì một trong các lý do sau:

  • Thiếu từ khóa Export: Namespace hoạt động giống như các object; mọi thứ bên trong đều là private trừ khi bạn đánh dấu chúng công khai một cách rõ ràng bằng từ khóa export.
  • Bẫy Module: File .d.ts của bạn đã trở thành một module vì nó chứa import hoặc export ở cấp cao nhất (top-level), làm mất khả năng truy cập toàn cục.
  • Cấu hình đường dẫn sai: File tsconfig.json không theo dõi thư mục chứa các khai báo (declarations) của bạn.

Các bước khắc phục chi tiết

Cách 1: Thêm từ khóa Export rõ ràng

Hãy coi namespace như một chiếc hộp đóng kín. Ngay cả khi bản thân chiếc hộp có thể truy cập được, các vật phẩm bên trong vẫn bị ẩn theo mặc định. Trong TypeScript, các thành viên bên trong một namespace là private trừ khi bạn thêm tiền tố export.

Kiểm tra file types.d.ts của bạn xem có mẫu này không:

// ❌ Sai: UserProfile bị ẩn bên trong namespace
declare namespace API {
  interface UserProfile {
    id: string;
    name: string;
  }
}

// ✅ Đúng: Từ khóa export giúp nó hiển thị ra ngoài
declare namespace API {
  export interface UserProfile {
    id: string;
    name: string;
  }
}

Cách 2: Xử lý "Bẫy Module"

Việc thêm một dòng import duy nhất ở đầu file khai báo (ví dụ: import { AxiosResponse } from 'axios') sẽ tạo ra một thay đổi lớn. TypeScript hiện sẽ coi file đó là một ES module thay vì một script toàn cục. Do đó, bạn sẽ mất khả năng sử dụng API.UserProfile trên toàn cầu.

Để khắc phục điều này, bạn phải export chính namespace đó và import nó ở bất cứ nơi nào cần thiết:

// types/AppTypes.ts
import { Request } from 'express';

export namespace AppTypes {
  export interface Context extends Request {
    userRole: 'admin' | 'user' | 'guest';
  }
}

Sau đó, sử dụng nó trong service của bạn giống như bất kỳ module nào khác:

import { AppTypes } from './types/AppTypes';

const role: AppTypes.Context['userRole'] = 'admin';

Cách 3: Cập nhật đường dẫn trong tsconfig.json

Đôi khi code của bạn hoàn hảo, nhưng trình biên dịch lại tìm kiếm sai chỗ. Nếu dự án của bạn có một thư mục tùy chỉnh cho các type, hãy đảm bảo tsconfig.json nhận biết được nó. Thêm src/**/*.d.ts vào mảng include là một cách đáng tin cậy để đảm bảo tất cả các khai báo đều được nhận diện.

{
  "compilerOptions": {
    "typeRoots": ["./node_modules/@types", "./src/types"],
    "baseUrl": ".",
    "paths": {
      "@types/*": ["src/types/*"]
    }
  },
  "include": [
    "src/**/*.ts",
    "src/**/*.d.ts"
  ]
}

Các bước xác minh

Sau khi áp dụng giải pháp, đừng chỉ đoán xem nó có hoạt động hay không. Hãy làm theo ba bước sau để xác nhận:

  • Khởi động lại TS Server: Các IDE thỉnh thoảng lưu lại các lỗi cũ. Trong VS Code, nhấn Cmd+Shift+P (macOS) hoặc Ctrl+Shift+P (Windows) và chạy lệnh "TypeScript: Restart TS Server".
  • Kiểm tra Tooltip: Di chuột qua namespace. Một thiết lập chuẩn sẽ hiển thị tooltip liệt kê mọi thành viên đã được export.
  • Chạy thử Build: Chạy npx tsc --noEmit. Đây là cách nhanh nhất để kiểm tra lỗi mà không cần đợi toàn bộ chu kỳ build.

Mẹo phòng ngừa

  • Ưu tiên sử dụng Module: Phát triển TypeScript hiện đại ưu tiên ES Modules (import/export) hơn namespaces. Chúng dễ theo dõi hơn và tốt hơn cho việc tree-shaking.
  • Giữ tính cục bộ: Nếu một type chỉ được sử dụng trong một component, hãy giữ nó ở đó. Các type toàn cục chỉ nên dành cho các data model hoặc cấu hình thư viện.
  • Chú ý hiện tượng Shadowing: Đảm bảo tên namespace của bạn không trùng với tên biến hoặc tên class, điều này có thể làm trình biên dịch bối rối.

Related Error Notes