TypeScript 命名空間 (Namespaces)
命名空間 (Namespaces) 是 TypeScript 提供的一種組織程式碼的方式,用來將相關的程式碼分組在一起,避免全域命名衝突。
在現代 TypeScript 開發中,通常建議使用 ES 模組 (import/export) 而非命名空間。命名空間主要用於組織大型專案中的型別定義,或是在沒有模組系統的環境中使用。
基本語法
使用 namespace 關鍵字定義命名空間:
namespace Geometry {
export const PI = 3.14159;
export function circleArea(radius: number): number {
return PI * radius * radius;
}
export function rectangleArea(width: number, height: number): number {
return width * height;
}
// 未 export 的成員只能在命名空間內部存取
function internalHelper(): void {
console.log('This is internal');
}
}
// 使用命名空間中的成員
console.log(Geometry.PI);
console.log(Geometry.circleArea(5));
// Geometry.internalHelper(); // 錯誤:未匯出
巢狀命名空間
namespace Company {
export namespace Departments {
export namespace Engineering {
export function getTeamSize(): number {
return 50;
}
}
export namespace Marketing {
export function getBudget(): number {
return 100000;
}
}
}
export function getInfo(): string {
return 'Company Inc.';
}
}
console.log(Company.Departments.Engineering.getTeamSize());
console.log(Company.Departments.Marketing.getBudget());
命名空間別名
使用 import 為巢狀命名空間建立別名:
namespace Shapes {
export namespace Polygons {
export class Triangle {
constructor(
public base: number,
public height: number
) {}
getArea(): number {
return (this.base * this.height) / 2;
}
}
export class Rectangle {
constructor(
public width: number,
public height: number
) {}
getArea(): number {
return this.width * this.height;
}
}
}
}
// 建立別名
import Polygons = Shapes.Polygons;
const triangle = new Polygons.Triangle(10, 5);
console.log(triangle.getArea());
跨檔案命名空間
命名空間可以跨多個檔案,編譯時會合併:
// shapes.ts
namespace Shapes {
export class Circle {
constructor(public radius: number) {}
getArea(): number {
return Math.PI * this.radius ** 2;
}
}
}
// more-shapes.ts
/// <reference path="shapes.ts" />
namespace Shapes {
export class Square {
constructor(public side: number) {}
getArea(): number {
return this.side ** 2;
}
}
}
// 使用時,Shapes 包含 Circle 和 Square
const circle = new Shapes.Circle(5);
const square = new Shapes.Square(4);
使用 tsconfig.json 合併
{
"compilerOptions": {
"outFile": "./dist/bundle.js"
},
"files": ["shapes.ts", "more-shapes.ts"]
}
命名空間與介面
namespace Validation {
export interface Validator {
isValid(value: string): boolean;
}
export class EmailValidator implements Validator {
isValid(value: string): boolean {
return /^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(value);
}
}
export class PhoneValidator implements Validator {
isValid(value: string): boolean {
return /^\d{10}$/.test(value);
}
}
}
const emailValidator = new Validation.EmailValidator();
console.log(emailValidator.isValid('test@example.com')); // true
const phoneValidator = new Validation.PhoneValidator();
console.log(phoneValidator.isValid('1234567890')); // true
命名空間與模組的差異
| 特性 | 命名空間 | ES 模組 |
|---|---|---|
| 語法 | namespace | import/export |
| 載入方式 | 編譯時合併 | 執行時載入 |
| 封裝方式 | 使用 export | 預設私有,使用 export 公開 |
| 檔案關係 | 可跨檔案同名 | 每個檔案是獨立模組 |
| 現代開發 | 較少使用 | 推薦使用 |
| 樹搖 (Tree Shaking) | 不支援 | 支援 |
何時使用命名空間
1. 組織型別定義
// types/api.ts
namespace API {
export namespace Response {
export interface User {
id: number;
name: string;
}
export interface Post {
id: number;
title: string;
content: string;
}
}
export namespace Request {
export interface CreateUser {
name: string;
email: string;
}
export interface UpdateUser {
name?: string;
email?: string;
}
}
}
// 使用
function getUser(): API.Response.User {
return { id: 1, name: '小明' };
}
function createUser(data: API.Request.CreateUser): void {
// ...
}
2. 擴充第三方函式庫
// 擴充 jQuery 的型別
declare namespace JQuery {
interface JQuery {
customPlugin(): JQuery;
}
}
3. 舊版程式碼相容
// 維護舊的程式碼庫
namespace LegacyApp {
export function initialize(): void {
console.log('Legacy app initialized');
}
export namespace Utils {
export function formatDate(date: Date): string {
return date.toLocaleDateString();
}
}
}
全域擴充
使用命名空間擴充全域物件:
// 擴充 Window 物件
declare global {
interface Window {
myApp: {
version: string;
init(): void;
};
}
}
window.myApp = {
version: '1.0.0',
init() {
console.log('App initialized');
},
};
export {}; // 確保這是一個模組
命名空間與類別
命名空間可以與同名的類別合併:
class Album {
label: Album.AlbumLabel;
constructor(label: Album.AlbumLabel) {
this.label = label;
}
}
namespace Album {
export interface AlbumLabel {
name: string;
year: number;
}
export function createLabel(name: string, year: number): AlbumLabel {
return { name, year };
}
}
const label = Album.createLabel('Sony', 2024);
const album = new Album(label);
命名空間與列舉
enum Color {
Red,
Green,
Blue,
}
namespace Color {
export function mixColors(c1: Color, c2: Color): Color {
// 簡化的混色邏輯
return Color.Green;
}
export function toHex(color: Color): string {
switch (color) {
case Color.Red:
return '#FF0000';
case Color.Green:
return '#00FF00';
case Color.Blue:
return '#0000FF';
}
}
}
console.log(Color.toHex(Color.Red)); // #FF0000
從命名空間遷移到模組
如果你正在維護使用命名空間的舊程式碼,以下是遷移建議:
// 之前:命名空間
namespace Utils {
export function formatDate(date: Date): string {
return date.toLocaleDateString();
}
export function formatNumber(num: number): string {
return num.toLocaleString();
}
}
// 之後:ES 模組
// utils/date.ts
export function formatDate(date: Date): string {
return date.toLocaleDateString();
}
// utils/number.ts
export function formatNumber(num: number): string {
return num.toLocaleString();
}
// utils/index.ts
export * from './date';
export * from './number';
總結
- 命名空間適合組織型別定義和維護舊程式碼
- 新專案建議使用 ES 模組 (
import/export) - 命名空間無法進行樹搖最佳化
- 命名空間可以跨檔案合併
- 命名空間可以與類別、列舉合併