فارسی

قدرت ادغام namespace در TypeScript را کشف کنید! این راهنما الگوهای پیشرفته تعریف ماژول برای ماژولار بودن، قابلیت توسعه و کد تمیزتر را بررسی می‌کند.

ادغام Namespace در TypeScript: الگوهای پیشرفته تعریف ماژول

تایپ‌اسکریپت ویژگی‌های قدرتمندی برای ساختاردهی و سازماندهی کد شما ارائه می‌دهد. یکی از این ویژگی‌ها ادغام namespace است که به شما امکان می‌دهد چندین namespace با نام یکسان تعریف کنید، و تایپ‌اسکریپت به‌طور خودکار تعاریف آن‌ها را در یک namespace واحد ادغام می‌کند. این قابلیت به‌ویژه برای توسعه کتابخانه‌های موجود، ایجاد برنامه‌های ماژولار و مدیریت تعاریف نوع پیچیده مفید است. این راهنما به بررسی الگوهای پیشرفته برای استفاده از ادغام namespace می‌پردازد و شما را قادر می‌سازد تا کد تایپ‌اسکریپت تمیزتر و قابل نگهداری‌تری بنویسید.

درک Namespaceها و ماژول‌ها

قبل از پرداختن به ادغام namespace، درک مفاهیم بنیادی namespaceها و ماژول‌ها در تایپ‌اسکریپت بسیار مهم است. در حالی که هر دو مکانیسم‌هایی برای سازماندهی کد فراهم می‌کنند، در دامنه و کاربردشان تفاوت‌های قابل توجهی دارند.

Namespaceها (ماژول‌های داخلی)

Namespaceها یک ساختار مخصوص تایپ‌اسکریپت برای گروه‌بندی کدهای مرتبط با هم هستند. آن‌ها اساساً کانتینرهای نام‌گذاری شده‌ای برای توابع، کلاس‌ها، اینترفیس‌ها و متغیرهای شما ایجاد می‌کنند. Namespaceها عمدتاً برای سازماندهی کد داخلی در یک پروژه تایپ‌اسکریپت استفاده می‌شوند. با این حال، با ظهور ماژول‌های ES، استفاده از namespaceها در پروژه‌های جدید عموماً کمتر ترجیح داده می‌شود، مگر اینکه به سازگاری با کدهای قدیمی‌تر یا سناریوهای خاص افزایش قابلیت‌های سراسری (global augmentation) نیاز داشته باشید.

مثال:


namespace Geometry {
  export interface Shape {
    getArea(): number;
  }

  export class Circle implements Shape {
    constructor(public radius: number) {}

    getArea(): number {
      return Math.PI * this.radius * this.radius;
    }
  }
}

const myCircle = new Geometry.Circle(5);
console.log(myCircle.getArea()); // Output: 78.53981633974483

ماژول‌ها (ماژول‌های خارجی)

از سوی دیگر، ماژول‌ها یک روش استاندارد برای سازماندهی کد هستند که توسط ماژول‌های ES (ماژول‌های اکمااسکریپت) و CommonJS تعریف شده‌اند. ماژول‌ها دامنه خاص خود را دارند و مقادیر را به صراحت وارد و صادر می‌کنند، که آن‌ها را برای ایجاد کامپوننت‌ها و کتابخانه‌های قابل استفاده مجدد ایده‌آل می‌سازد. ماژول‌های ES استاندارد توسعه جاوااسکریپت و تایپ‌اسکریپت مدرن هستند.

مثال:


// circle.ts
export interface Shape {
  getArea(): number;
}

export class Circle implements Shape {
  constructor(public radius: number) {}

  getArea(): number {
    return Math.PI * this.radius * this.radius;
  }
}

// app.ts
import { Circle } from './circle';

const myCircle = new Circle(5);
console.log(myCircle.getArea());

قدرت ادغام Namespace

ادغام Namespace به شما امکان می‌دهد چندین بلوک کد را با نام namespace یکسان تعریف کنید. تایپ‌اسکریپت به طور هوشمند این تعاریف را در زمان کامپایل در یک namespace واحد ادغام می‌کند. این قابلیت برای موارد زیر بسیار ارزشمند است:

الگوهای پیشرفته تعریف ماژول با ادغام Namespace

بیایید برخی از الگوهای پیشرفته برای استفاده از ادغام namespace در پروژه‌های تایپ‌اسکریپت شما را بررسی کنیم.

۱. توسعه کتابخانه‌های موجود با تعریف‌های محیطی

یکی از رایج‌ترین موارد استفاده از ادغام namespace، توسعه کتابخانه‌های جاوااسکریپت موجود با تعاریف نوع تایپ‌اسکریپت است. تصور کنید از یک کتابخانه جاوااسکریپت به نام `my-library` استفاده می‌کنید که پشتیبانی رسمی تایپ‌اسکریپت ندارد. شما می‌توانید یک فایل تعریف محیطی (مثلاً `my-library.d.ts`) برای تعریف انواع این کتابخانه ایجاد کنید.

مثال:


// my-library.d.ts
declare namespace MyLibrary {
  interface Options {
    apiKey: string;
    timeout?: number;
  }

  function initialize(options: Options): void;
  function fetchData(endpoint: string): Promise;
}

اکنون، می‌توانید از namespace `MyLibrary` در کد تایپ‌اسکریپت خود با ایمنی نوع (type safety) استفاده کنید:


// app.ts
MyLibrary.initialize({
  apiKey: 'YOUR_API_KEY',
  timeout: 5000,
});

MyLibrary.fetchData('/api/data')
  .then(data => {
    console.log(data);
  });

اگر بعداً نیاز به افزودن قابلیت‌های بیشتری به تعاریف نوع `MyLibrary` داشتید، می‌توانید به سادگی یک فایل `my-library.d.ts` دیگر ایجاد کنید یا به فایل موجود اضافه کنید:


// my-library.d.ts

declare namespace MyLibrary {
  interface Options {
    apiKey: string;
    timeout?: number;
  }

  function initialize(options: Options): void;
  function fetchData(endpoint: string): Promise;

  // افزودن یک تابع جدید به namespace MyLibrary
  function processData(data: any): any;
}

تایپ‌اسکریپت به طور خودکار این تعاریف را ادغام می‌کند و به شما امکان می‌دهد از تابع جدید `processData` استفاده کنید.

۲. افزودن قابلیت به اشیاء سراسری (Global Objects)

گاهی اوقات، ممکن است بخواهید خصوصیات یا متدهایی را به اشیاء سراسری موجود مانند `String`، `Number` یا `Array` اضافه کنید. ادغام Namespace به شما امکان می‌دهد این کار را با ایمنی و با بررسی نوع انجام دهید.

مثال:


// string.extensions.d.ts
declare global {
  interface String {
    reverse(): string;
  }
}

String.prototype.reverse = function() {
  return this.split('').reverse().join('');
};

console.log('hello'.reverse()); // Output: olleh

در این مثال، ما یک متد `reverse` به پروتوتایپ `String` اضافه می‌کنیم. سینتکس `declare global` به تایپ‌اسکریپت می‌گوید که ما در حال تغییر یک شیء سراسری هستیم. مهم است توجه داشته باشید که اگرچه این کار ممکن است، افزودن قابلیت به اشیاء سراسری گاهی اوقات می‌تواند منجر به تداخل با کتابخانه‌های دیگر یا استانداردهای آینده جاوااسکریپت شود. از این تکنیک با احتیاط استفاده کنید.

ملاحظات بین‌المللی‌سازی: هنگام افزودن قابلیت به اشیاء سراسری، به ویژه با متدهایی که رشته‌ها یا اعداد را دستکاری می‌کنند، به بین‌المللی‌سازی توجه داشته باشید. تابع `reverse` بالا برای رشته‌های ساده ASCII کار می‌کند، اما ممکن است برای زبان‌هایی با مجموعه‌های کاراکتری پیچیده یا جهت نوشتاری راست-به-چپ مناسب نباشد. برای دستکاری رشته‌های آگاه از منطقه (locale-aware)، استفاده از کتابخانه‌هایی مانند `Intl` را در نظر بگیرید.

۳. ماژولار کردن Namespaceهای بزرگ

هنگام کار با namespaceهای بزرگ و پیچیده، شکستن آن‌ها به فایل‌های کوچکتر و قابل مدیریت‌تر مفید است. ادغام Namespace این کار را آسان می‌کند.

مثال:


// geometry.ts
namespace Geometry {
  export interface Shape {
    getArea(): number;
  }
}

// circle.ts
namespace Geometry {
  export class Circle implements Shape {
    constructor(public radius: number) {}

    getArea(): number {
      return Math.PI * this.radius * this.radius;
    }
  }
}

// rectangle.ts
namespace Geometry {
  export class Rectangle implements Shape {
    constructor(public width: number, public height: number) {}

    getArea(): number {
      return this.width * this.height;
    }
  }
}

// app.ts
/// 
/// 
/// 

const myCircle = new Geometry.Circle(5);
const myRectangle = new Geometry.Rectangle(10, 5);

console.log(myCircle.getArea()); // Output: 78.53981633974483
console.log(myRectangle.getArea()); // Output: 50

در این مثال، ما namespace `Geometry` را به سه فایل تقسیم کرده‌ایم: `geometry.ts`، `circle.ts` و `rectangle.ts`. هر فایل به namespace `Geometry` کمک می‌کند و تایپ‌اسکریپت آن‌ها را با هم ادغام می‌کند. به استفاده از دستورات `/// ` توجه کنید. اگرچه این دستورات کار می‌کنند، اما یک رویکرد قدیمی‌تر هستند و استفاده از ماژول‌های ES در پروژه‌های مدرن تایپ‌اسکریپت، حتی هنگام استفاده از namespaceها، عموماً ترجیح داده می‌شود.

رویکرد مدرن ماژول (ترجیح داده شده):


// geometry.ts
export namespace Geometry {
  export interface Shape {
    getArea(): number;
  }
}

// circle.ts
import { Geometry } from './geometry';

export namespace Geometry {
  export class Circle implements Shape {
    constructor(public radius: number) {}

    getArea(): number {
      return Math.PI * this.radius * this.radius;
    }
  }
}

// rectangle.ts
import { Geometry } from './geometry';

export namespace Geometry {
  export class Rectangle implements Shape {
    constructor(public width: number, public height: number) {}

    getArea(): number {
      return this.width * this.height;
    }
  }
}

// app.ts
import { Geometry } from './geometry';
const myCircle = new Geometry.Circle(5);
const myRectangle = new Geometry.Rectangle(10, 5);

console.log(myCircle.getArea());
console.log(myRectangle.getArea());

این رویکرد از ماژول‌های ES به همراه namespaceها استفاده می‌کند و ماژولاریتی بهتر و سازگاری با ابزارهای مدرن جاوااسکریپت را فراهم می‌کند.

۴. استفاده از ادغام Namespace با افزایش قابلیت اینترفیس (Interface Augmentation)

ادغام Namespace اغلب با افزایش قابلیت اینترفیس ترکیب می‌شود تا قابلیت‌های انواع موجود را گسترش دهد. این به شما امکان می‌دهد خصوصیات یا متدهای جدیدی را به اینترفیس‌های تعریف شده در کتابخانه‌ها یا ماژول‌های دیگر اضافه کنید.

مثال:


// user.ts
interface User {
  id: number;
  name: string;
}

// user.extensions.ts
namespace User {
  export interface User {
    email: string;
  }
}

// app.ts
import { User } from './user'; // با فرض اینکه user.ts اینترفیس User را صادر می‌کند
import './user.extensions'; // وارد کردن برای اثر جانبی: افزودن قابلیت به اینترفیس User

const myUser: User = {
  id: 123,
  name: 'John Doe',
  email: 'john.doe@example.com',
};

console.log(myUser.name);
console.log(myUser.email);

در این مثال، ما با استفاده از ادغام namespace و افزایش قابلیت اینترفیس، یک خصوصیت `email` به اینترفیس `User` اضافه می‌کنیم. فایل `user.extensions.ts` اینترفیس `User` را توسعه می‌دهد. به وارد کردن `./user.extensions` در `app.ts` توجه کنید. این import صرفاً برای اثر جانبی آن یعنی افزودن قابلیت به اینترفیس `User` است. بدون این import، افزایش قابلیت اعمال نخواهد شد.

بهترین شیوه‌ها برای ادغام Namespace

در حالی که ادغام namespace یک ویژگی قدرتمند است، استفاده محتاطانه از آن و پیروی از بهترین شیوه‌ها برای جلوگیری از مشکلات احتمالی ضروری است:

ملاحظات جهانی

هنگام توسعه برنامه‌ها برای مخاطبان جهانی، ملاحظات زیر را هنگام استفاده از ادغام namespace در نظر داشته باشید:

مثال بومی‌سازی با `Intl` (API بین‌المللی‌سازی):


// number.extensions.d.ts
declare global {
  interface Number {
    toCurrencyString(locale: string, currency: string): string;
  }
}

Number.prototype.toCurrencyString = function(locale: string, currency: string) {
  return new Intl.NumberFormat(locale, {
    style: 'currency',
    currency: currency,
  }).format(this);
};

const price = 1234.56;

console.log(price.toCurrencyString('en-US', 'USD')); // Output: $1,234.56
console.log(price.toCurrencyString('de-DE', 'EUR')); // Output: 1.234,56 €
console.log(price.toCurrencyString('ja-JP', 'JPY')); // Output: ¥1,235

این مثال نشان می‌دهد که چگونه می‌توان یک متد `toCurrencyString` را با استفاده از API `Intl.NumberFormat` به پروتوتایپ `Number` اضافه کرد، که به شما امکان می‌دهد اعداد را بر اساس مناطق و ارزهای مختلف قالب‌بندی کنید.

نتیجه‌گیری

ادغام namespace در تایپ‌اسکریپت ابزاری قدرتمند برای توسعه کتابخانه‌ها، ماژولار کردن کد و مدیریت تعاریف نوع پیچیده است. با درک الگوهای پیشرفته و بهترین شیوه‌های ذکر شده در این راهنما، می‌توانید از ادغام namespace برای نوشتن کد تایپ‌اسکریپت تمیزتر، قابل نگهداری‌تر و مقیاس‌پذیرتر استفاده کنید. با این حال، به یاد داشته باشید که ماژول‌های ES اغلب رویکرد ترجیحی برای پروژه‌های جدید هستند و ادغام namespace باید به صورت استراتژیک و محتاطانه استفاده شود. همیشه پیامدهای جهانی کد خود را، به ویژه هنگام کار با بومی‌سازی، کدگذاری کاراکترها و قراردادهای فرهنگی، در نظر بگیرید تا اطمینان حاصل کنید که برنامه‌های شما برای کاربران در سراسر جهان قابل دسترسی و قابل استفاده هستند.