آموزش استفاده از Namespaceها در TypeScript

TypeScript یک توسعه از زبان JavaScript است که از موتور زمان اجرا جاوااسکریپت بهره می‌برد ولی با یک نوع بررسی در زمان کامپایل همراه است. در TypeScript می‌توانید از Namespaces برای سازماندهی کد استفاده کنید. این مفهوم که قبلاً با نام internal modules شناخته می‌شد، بر اساس پیش‌نویس اولیه ماژول‌های ECMAScript است. در پیش‌نویس ECMAScript که حدود سپتامبر ۲۰۱۳ منتشر شد، internal modules حذف شدند اما TypeScript این ایده را با نام متفاوت حفظ کرده است.

Namespaces به شما امکان می‌دهد واحدهای سازمانی جداگانه‌ای بسازید که می‌توانند شامل مقادیر متعددی مانند properties، کلاس‌ها، تایپ‌ها و interface‎ها باشند. در این آموزش، نحوه ایجاد و استفاده از Namespaces بررسی می‌شود و نمونه کدهایی برای تعریف و تلفیق Namespaces ، روش کار Namespaces در جاوااسکریپت خروجی و کاربرد آنها برای تعریف انواع داده‌ها در کتابخانه‌های خارجی ارائه می‌شود.

ایجاد Namespace در TypeScript

برای ایجاد namespace، از کلیدواژه namespace به همراه نام آن و سپس یک بلاک {} استفاده می‌کنید. به عنوان مثال، یک namespace با نام DatabaseEntity ایجاد می‌کنیم تا موجودیت‌های مرتبط با پایگاه داده را نگهداری کند، مثل نمونه‌ای از یک کتابخانه ORM:

namespace DatabaseEntity {
    // code will be added here
}

حالا یک کلاس User به داخل این namespace اضافه کنید که نمایانگر یک موجودیت کاربر در دیتابیس است:

namespace DatabaseEntity {
    export class User {
        constructor(public name: string, public email: string) {}
    }
}

شما می‌توانید از کلاس User به صورت معمول داخل namespace استفاده کنید. نمونه زیر یک نمونه جدید از کلاس User ایجاد می‌کند و آن را در متغیر newUser ذخیره می‌کند:

namespace DatabaseEntity {
    export class User {
        constructor(public name: string, public email: string) {}
    }

    export const newUser = new User("Ali", "ali@example.com");
}

کلاس User اکنون با پیشوند namespace قابل دسترسی است. چنانچه بخواهید از کلاس User خارج از namespace استفاده کنید، باید آن را با کلیدواژه export صادر کنید.

دسترسی به کلاس User خارج از namespace به صورت زیر ممکن است:

const user = new DatabaseEntity.User("Sara", "sara@example.com");

همچنین می‌توانید متغیرها را از داخل namespace صادر کنید، که به آن‌ها به صورت property های namespace دسترسی خواهید داشت. نمونه زیر متغیر newUser را صادر می‌کند:

namespace DatabaseEntity {
    export class User {
        constructor(public name: string, public email: string) {}
    }

    export const newUser = new User("Ali", "ali@example.com");
}

console.log(DatabaseEntity.newUser);

اجرای کد بالا مقدار زیر را در کنسول چاپ می‌کند:

User { name: "Ali", email: "ali@example.com" }

ادغام Declaration های Namespace

در TypeScript می‌توانید چند بار namespace یکسانی با نام مشابه تعریف کنید و در نهایت این declaration های جداگانه با هم ادغام می‌شوند. این ویژگی امکان افزایش ویژگی‌های یک namespace را فراهم می‌کند.

مثلاً ما می‌توانیم namespace DatabaseEntity را دوباره تعریف کرده و یک کلاس جدید به نام UserRole را به آن اضافه کنیم:

namespace DatabaseEntity {
    export class UserRole {
        constructor(public user: User, public role: string) {}
    }

    export const userRole = new UserRole(newUser, "admin");
}

داخل این declaration جدید می‌توانید به تمام member های صادر شده از قبل در DatabaseEntity دسترسی داشته باشید بدون اینکه نیاز به نام fully qualified باشد.

ترجمه Namespaceها به جاوااسکریپت

Namespaces در TypeScript فقط یک ویژگی compile-time نیست و روی کد خروجی جاوااسکریپت هم تأثیر می‌گذارد. برای بررسی، کد TypeScript زیر را:

namespace DatabaseEntity {
    export class User {
        constructor(public name: string) {}
    }
}

کامپایلر TypeScript به شکل زیر ترجمه می‌کند:

var DatabaseEntity;
(function (DatabaseEntity) {
    class User {
        constructor(name) {
            this.name = name;
        }
    }
    DatabaseEntity.User = User;
})(DatabaseEntity || (DatabaseEntity = {}));

یک متغیر DatabaseEntity تعریف می‌شود که اگر مقدار نداشت به یک شیء خالی مقداردهی می‌شود. سپس با یک IIFE اجرا شده و کلاس User به عنوان property به این شیء اضافه می‌شود.

در صورت استفاده از چند declaration برای یک namespace، چند IIFE ایجاد می‌شود ولی همه آن‌ها روی شیء DatabaseEntity موجود تغییر ایجاد می‌کنند:

var DatabaseEntity;
(function (DatabaseEntity) {
    class User {
        constructor(name) {
            this.name = name;
        }
    }
    DatabaseEntity.User = User;
})(DatabaseEntity || (DatabaseEntity = {}));

(function (DatabaseEntity) {
    class UserRole {
        constructor(user, role) {
            this.user = user;
            this.role = role;
        }
    }
    DatabaseEntity.UserRole = UserRole;
})(DatabaseEntity || (DatabaseEntity = {}));

ایجاد Type Declaration برای کتابخانه‌های خارجی بدون تایپ

در پروژه‌های واقعی ممکن است کتابخانه‌هایی داشته باشید که همراه با تایپ‌های TypeScript ارائه نشده‌اند. در این صورت کامپایلر خطایی مبنی بر عدم وجود تایپ می‌دهد که باید با ایجاد declaration خودتان آن را رفع کنید.

فرض کنید کتابخانه‌ای به نام example-vector3 دارید که فقط یک کلاس Vector3 با متدی به نام add صادر می‌کند:

export class Vector3 {
    constructor(public x: number, public y: number, public z: number) {}
    add(v: Vector3): Vector3 {
        // returns a new Vector3 with component wise addition
    }
}

اگر این کتابخانه تایپ نداشته باشد و شما بخواهید از آن در پروژه خود استفاده کنید، با خطای زیر مواجه می‌شوید:

error TS2307: Cannot find module 'example-vector3'.

برای رفع این خطا، باید یک فایل تایپ به مسیر types/example-vector3/index.d.ts اضافه کنید:

declare module "example-vector3" {
    export namespace vector3 {
        // content will be added here
    }
}

سپس این مسیر را به فایل tsconfig.json پروژه اضافه کنید تا TypeScript آن را شناسایی نماید:

{
  "compilerOptions": {
    "types": ["example-vector3"]
  }
}

اکنون اگر بخواهید کلاس Vector3 را از namespace خالی صادر شده استفاده کنید، خطای جدید ظاهر می‌شود که به دلیل خالی بودن محتویات namespace است.

فایل declaration را با تعریف کلاس Vector3 کامل کنید:

declare module "example-vector3" {
    export namespace vector3 {
        export class Vector3 {
            constructor(x: number, y: number, z: number);
            add(v: Vector3): Vector3;
        }
    }
}

این کد فقط تایپ‌ها را تعریف می‌کند بدون پیاده‌سازی (ambient declaration). اینگونه تایپ‌ها به شما اجازه می‌دهد از این کتابخانه‌ها به صورت تایپ‌سیف استفاده کنید.

نتیجه‌گیری

در این آموزش با Syntax پایه Namespaceها در TypeScript آشنا شدید و نحوه تبدیل آن به جاوااسکریپت را دیدید. همچنین کاربرد مهم Namespaceها در تعریف تایپ‌های خارجی بررسی شد.

هرچند استفاده از Namespaceها برای سازماندهی کدهای جدید توصیه نمی‌شود و بهتر است از استاندارد ماژول‌های ES استفاده شود، ولی برای ایجاد module declarations تایپی، Namespaceها همچنان کاربرد دارد و باعث ساده‌تر شدن این کار می‌شود.

از اینکه با پارمین کلود همراهید متشکریم.

Click to rate this post!
[Total: 0 Average: 0]

نظرات کاربران

دیدگاهی بنویسید

نشانی ایمیل شما منتشر نخواهد شد. بخش‌های موردنیاز علامت‌گذاری شده‌اند *