الـ namespace كتلة مسماة تجمع دوالًا وثوابت وأنواعًا تحت اسم واحد. الأعضاء المميزون بـ export يمكن الوصول إليهم بالشكل Name.member؛ والبقية تبقى خاصة بالكتلة. وقت التشغيل يكون الـ namespace كائنًا عاديًا.
إلى ماذا يُترجم Namespace
الـ namespaces من الميزات القليلة في TypeScript التي تولّد كودًا. الكتلة السابقة تصبح دالة تملأ كائنًا:
var Geometry;
(function (Geometry) {
const TAU = Math.PI * 2;
function circumference(radius) {
return TAU * radius;
}
Geometry.circumference = circumference;
function area(radius) {
return Math.PI * radius ** 2;
}
Geometry.area = area;
})(Geometry || (Geometry = {}));
هذا هو النمط الذي استخدمه كود JavaScript قبل وجود الوحدات لتجنب وضع كل اسم في النطاق العام. TAU متغير محلي في الدالة، ولهذا لا يستطيع الكود الآخر رؤيته. ومحاولة قراءته خطأ وقت الترجمة:
يبلّغ المترجم عن index.ts(10,17): error TS2339: Property 'rate' does not exist on type 'typeof Tax'. أضف export قبل const rate فيعمل السطران.
التداخل والدمج والأسماء البديلة
يمكن أن تتداخل الـ namespaces، وكتلتان بالاسم نفسه تندمجان في واحدة. التعبير Geometry || (Geometry = {}) في الناتج هو ما يجعل ذلك ممكنًا: الكتلة الثانية تضيف إلى الكائن الموجود. وينشئ import X = A.B اسمًا بديلًا قصيرًا.
namespace A.B.C { } اختصار لثلاث كتل متداخلة. والكتابة القديمة module Shop { } تعني الشيء نفسه لكنها مرفوضة الآن بالخطأ TS1540، A 'namespace' declaration should not be declared using the 'module' keyword. Please use the 'namespace' keyword instead.
الدمج مع الدوال والأصناف
يمكن أن يشترك namespace في اسمه مع دالة أو صنف أو enum ويضيف إليه أعضاء. وما زالت هذه أنظف طريقة لوصف دالة تحمل أيضًا خصائص، أو صنف ملحقة به دوال مساعدة.
في الصنف تؤدي الدالة static المهمة نفسها وهي JavaScript عادية. وفي الدالة يمكنك أيضًا الاستغناء عن الـ namespace وإسناد format.prefix = "$" مباشرة بعد التعريف؛ فـ TypeScript يتتبع الخصائص المسندة بهذه الطريقة.
الـ Namespaces مقابل الوحدات
قبل وحدات ES كان برنامج TypeScript الكبير عبارة عن ملفات script كثيرة تتشارك namespaces عامة، تُربط معًا بـ /// <reference path="..." /> وتُترجم إلى ملف واحد بـ outFile. حلّت الوحدات محل ذلك: كل ملف له نطاقه الخاص، والتبعيات استيرادات صريحة، وأدوات التجميع تستطيع حذف التصديرات غير المستخدمة. أزال TypeScript 7 الخيار outFile (الخطأ TS5102)، فلم يعد إعداد الـ namespaces متعددة الملفات خيارًا للبناء.
| Namespace | الوحدة (Module) | |
|---|---|---|
| الوحدة الأساسية | كتلة مسماة في ملف | الملف نفسه |
| النطاق | عام ما لم يكن داخل وحدة | خاص بها دائمًا |
| التبعيات | ضمنية، حسب ترتيب التحميل | import صريح |
| الناتج | كائن تبنيه دالة | import/export أو require |
| حذف الكود غير المستخدم | تُبقي أدوات التجميع كل عضو | تستطيع أدوات التجميع حذف التصديرات غير المستخدمة |
| يعمل مع حذف الأنواع في Node | فقط إذا كان يحتوي على أنواع فقط | نعم |
داخل وحدة، تغليف كل شيء في namespace يضيف مستوى ثانيًا من التسمية دون فائدة: سيكتب المستوردون Utils.Utils.format. صدّر الدوال مباشرة ودع المستورد يختار import * as Utils from "./utils.js" إذا أراد بادئة.
أين ما زلت تصادف الـ Namespaces
تستخدمها ملفات التعريفات لوصف المكتبات التي تكشف كائنًا عامًا واحدًا، ولتجميع الأنواع:
// jquery-like.d.ts: a global function that also has properties
declare function $(selector: string): unknown;
declare namespace $ {
const version: string;
function ajax(url: string): Promise<unknown>;
}
وتستخدمها حزم الأنواع لكشف أنواع يمكنك توسيعها. تعرّف @types/node الـ namespace NodeJS، والإضافة إلى الواجهة ProcessEnv فيه من أي وحدة تحتاج إلى declare global:
// env.d.ts
export {};
declare global {
namespace NodeJS {
interface ProcessEnv {
API_URL: string; // process.env.API_URL is now string, not string | undefined
}
}
}
الواجهات داخل الـ namespaces المندمجة تندمج أيضًا، وهذا ما يجعل هذا التوسيع يعمل.
الـ Namespaces وحذف الأنواع
تشغّل Node 24 ملفات .ts بحذف صياغة الأنواع. الـ namespace الذي فيه قيم لا يمكن حذفه، بل يجب ترجمته إلى الكائن الموضح سابقًا، لذلك يتوقف node app.ts مع:
SyntaxError [ERR_UNSUPPORTED_TYPESCRIPT_SYNTAX]: TypeScript namespace declaration is not supported in strip-only mode
نوعان من الـ namespaces لا مشكلة فيهما لأنهما يختفيان تمامًا: declare namespace، والـ namespace الذي كل أعضائه أنواع أو واجهات. يبلّغ خيار المترجم erasableSyntaxOnly: true عن الأنواع الأخرى وقت الترجمة بالخطأ TS1294، This syntax is not allowed when 'erasableSyntaxOnly' is enabled.، وهكذا تُبعدها المشاريع التي تعمل بحذف الأنواع. أما node --experimental-transform-types فيترجم الـ namespaces، مع تحذير بأن الميزة تجريبية.
الأسئلة الشائعة
هل أستخدم namespaces أم الوحدات في TypeScript؟
استخدم الوحدات (import وexport) للكود الجديد. كل ملف له نطاقه الخاص أصلًا، وأدوات التجميع وNode تفهم الوحدات، ويمكن حذف التصديرات غير المستخدمة. تبقى الـ namespaces مفيدة في ملفات التعريفات، وللتوسيع العام، ولإلحاق أنواع أو دوال مساعدة بدالة أو صنف يحمل الاسم نفسه.
إلى ماذا يُترجم namespace في TypeScript؟
إلى كائن تملؤه دالة تُستدعى فورًا: var Geometry; (function (Geometry) { Geometry.circle = circle; })(Geometry || (Geometry = {}));. الأعضاء المصدَّرون يصبحون خصائص لذلك الكائن؛ والأعضاء دون export يبقون محليين داخل الدالة.
ما الفرق بين namespace وmodule في TypeScript؟
الوحدة (module) ملف فيه import أو export على المستوى الأعلى. أما الـ namespace فكتلة مسماة داخل ملف. كان TypeScript القديم يسمي الـ namespaces "وحدات داخلية" ويسمح بـ module Foo {}؛ هذه الكتابة الآن خطأ TS1540، والمقبول فقط namespace Foo {}.
هل تستطيع Node تشغيل ملفات TypeScript التي تستخدم namespaces؟
ليس مع حذف الأنواع الافتراضي فيها. الـ namespace الذي يحتوي على قيم ينتج ERR_UNSUPPORTED_TYPESCRIPT_SYNTAX، لأن الحذف يزيل الأنواع فقط بينما يحتاج الـ namespace إلى كود مولَّد. أما الـ namespaces التي لا تحتوي إلا على أنواع، وdeclare namespace، فتُمحى وتعمل دون مشكلة.