مبانی TypeScript؛ ماژول‌ها
اندازه‌ی قلم متن
تخمین مدت زمان مطالعه‌ی مطلب: چهار دقیقه

تاریخچه

تا پیش از نگارش 1.5 تایپ اسکریپت، مفاهیم internal modules و external modules وجود داشتند. جهت نامگذاری بهتر و کاهش سردرگمی در استفاده‌ی آن‌ها، از نگارش 1.5 به بعد، ماژول‌های داخلی به namespaces (فضاهای نام) تغییر نام یافتند و ماژول‌های خارجی به نام «ماژول» خلاصه شدند.
همچنین از نگارش 1.5 به بعد، پشتیبانی کاملی از نحوه‌ی تعریف «ماژول‌ها در ES 6» نیز به عمل می‌آید. بنابراین مطالعه‌ی آن نیز پیشنهاد می‌گردد.


مفهوم ماژول‌ها

هدف اصلی از ماژول‌ها، ارائه‌ی روشی برای مدیریت و ساماندهی پروژه‌های بزرگ با تعداد فایل‌های زیاد است. در اینجا فایل‌های ارجاعی، در زمان اجرا، توسط runtime جاوا اسکریپت بارگذاری شده و سپس به امکانات آن‌ها دسترسی خواهیم داشت. ماژول‌ها به صورت توکار در Node.JS نیز پشتیبانی می‌شوند؛ البته با فرمت common.js که کامپایلر TypeScript نیز قادر به تولید آن است.


امکان کامپایل به روش‌های قدیمی‌تر تعریف ماژول‌ها در TypeScript

در مورد انواع روش‌های قدیمی‌تر نحوه‌ی تعریف ماژول‌های در جاوا اسکریپت مانند common.js، AMD و امثال آن‌ها، مطالعه‌ی مطلب «ماژول‌ها در ES 6» توصیه می‌شود. فقط نکته‌ای که در اینجا حائز اهمیت است، این است که چون TypeScript قادر است به ES 5 نیز کامپایل شود و در ES 5 روش جدید ES 6 جهت تعریف ماژول‌ها وجود ندارد، امکان تبدیل و ترجمه‌ی کدهای TypeScript به تمام نوع‌های معروف و شناخته شده‌ی ماژول‌ها مانند common.js توسط کامپایلر TypeScript به صورت خودکار وجود دارد. برای این منظور از سوئیچ module کامپایلر استفاده می‌شود.


نحوه‌ی تعریف ماژول‌ها در TypeScript

برای تبدیل یک فایل ts به یک ماژول، تنها کافی است موردی را از آن export کنیم. آیتم‌های موجود در یک ماژول، تنها زمانی در سایر فایل‌ها قابل استفاده خواهند بود که از آن export شده باشند:
 // periodicals.ts
export interface Periodical {
   issueNumber: number;
}

export class Magazine implements Periodical {
   issueNumber: number;
}

export function GetMagazineByIssueNumber(issue: number): Magazine {
   // retrieve and return a magazine
}
در این مثال، یک اینترفیس، کلاس و متد export شده‌اند. برای این منظور واژه‌ی کلیدی export به پیش از هر کدام از آیتم‌های مدنظر اضافه شده‌است.
روش دیگر انجام این تعاریف، حذف واژه‌ی کلیدی export از تمام موارد تعریف شده و سپس خلاصه کردن آن‌ها در یک سطر، توسط روش export statement است؛ به نحو ذیل:
 // periodicals.ts
interface Periodical {
   issueNumber: number;
}

class Magazine implements Periodical {
   issueNumber: number;
}

function GetMagazineByTitle(title: string): Magazine {
   // retrieve and return a magazine
}

export { Periodical, Magazine, GetMagazineByTitle as GetMag}
مزیت این روش، مشخص بودن محل تعاریف خروجی‌ها است؛ بدون اینکه نیازی باشد تا تمام فایل‌را جهت یافتن exportها جستجو کرد.
همچنین در اینجا می‌توان نام دیگری را نیز برای خروجی‌ها درنظر گرفت. برای مثال بجای نام GetMagazineByTitle، با استفاده از as syntax، یک نام جدید معرفی شده‌است.


نحوه‌ی استفاده‌ی از ماژول‌ها در TypeScript

برای استفاده‌ی از امکانات خروجی مثال قبل، در یک ماژول دیگر، به نحو ذیل عمل می‌کنیم:
 // news.ts
import { Magazine, GetMag as GetMagazine} from './periodicals';
let newsMag: Magazine = GetMagazine('Weekly News');
در اینجا پس از تعریف واژه‌ی کلیدی import، لیست موارد مدنظر از خروجی‌های فایل periodicals را داخل یک {} می‌توان قید کرد. بنابراین نیازی نیست تا تمام خروجی‌های یک ماژول را import کرد. همچنین در اینجا نیز با استفاده از as syntax می‌توان نام جدیدی را برای موارد import شده تعیین کرد.
در انتها نیز مسیر نسبی فایل ts ماژول، بدون ذکر پسوند آن، پس از واژه‌ی کلیدی from ذکر می‌شود.

اگر نیاز است تمام خروجی‌های یک ماژول به صورت خودکار import شوند، می‌توان از * استفاده کرد:
 // kids.ts
import * as mag from './periodicals';
اینبار با توجه به as syntax استفاده شده، نحوه‌ی دسترسی به خروجی‌های ماژول مدنظر به صورت ذیل خواهد بود (ابتدا ذکر نام alias تعریف شده، به همراه یک دات):
 let kidMag: mag.Magazine= mag.GetMag('Games and Stuff!');


خروجی پیش فرض یک ماژول

اگر تنها قرار است یک آیتم از ماژولی export شود، می‌توان از مفهوم default export استفاده کرد:
 // movie.ts
export default class{
   title: string;
   director: string;
}
در این مثال export default بر روی یک کلاس بدون نام تعریف شده‌است. تعریف نام کلاس در اینجا اختیاری است و ماژول import کننده‌ی آن نیازی به دانستن این نام ندارد؛ زیرا در این حالت import کننده می‌تواند نام دلخواهی را به این خروجی پیش فرض بدهد؛ مانند AnimatedMovie بدون نیاز به ذکر {}:
 // kids.ts
import AnimatedMovie from './movie’;
let cartoon = new AnimatedMovie();
  • #
    ‫۸ سال و ۳ ماه قبل، دوشنبه ۷ تیر ۱۳۹۵، ساعت ۰۱:۱۰
    درود جناب نصیری ، من یک مشکلی که دارم و الان یک ساعته که دارم R&D میکنم و موفق به حلش نشدم متاسفانه این هست که یک فایل جدید ts درست در روت پروژه درست کردم ( کلا پروزه شامل دو فایل app.ts و testmd.ts هست ) فایل testmd  یک کلاس رو تعریف کرده و اون رو export کرده و در  app اون رو import کردم و سعی دارم از اون کلاس استفاده کنم و درواقع خواستم این مطلب رو تست کنم و یک ماژول آزمایشی درست کنم و ازش استفاده کنم . هر کاری کردم پروژه بیلد نمیشه و خطای  Error TS2307 Cannot find module 'testmd'  در تب Error نمایش داده میشه . هر چی هم جستجو کردم به نتیجه ای نرسیدم واقعا. 
    فایل پروژه هم اتچ کردم ، اگر ممکنه راهنمایی کنید خیلی ممنون میشم . 
    محیط توسعه :
    + Visual Studio 2015 Update 2
    + آخرین نگارش Resharper که متناسب با نسخه TypeScript که 1.8 هست تنظیمش کردم ( چک کردم و نسخه‌های دیگه TypeScript  رو نصب ندارم)
    + Web Essentials 2015.3 v3.0.230 ، Web Extension Pack v1.4.44 ، Web Analyzer v1.7.77 
    با تشکر 
    • #
      ‫۸ سال و ۳ ماه قبل، دوشنبه ۷ تیر ۱۳۹۵، ساعت ۰۱:۵۸
      مسیر ذکر نام فایل ماژول را با /. شروع کنید:
      import {Book} from "./testmd";
      • #
        ‫۸ سال و ۳ ماه قبل، دوشنبه ۷ تیر ۱۳۹۵، ساعت ۰۲:۲۲
        خیلی ممنون ، الان پروژه کامپایل شد ولی نتیجه ای که باید اتفاق میوفتاد نیوفتاد . توی developer tools وقتی Console Log مرورگر رو نگاه کردم موارد زیر خطا گرفته شده بود 
        testmd.ts:1Uncaught ReferenceError: exports is not defined
        require.js:143 Uncaught Error: Module name "testmd" has not been loaded yet for context: _. Use require([])

        کتابخانه RequireJs رو هم به پروژه اضافه کردم حالا محتوای فایل html برابر با موارد زیر هست 
        <!DOCTYPE html>
        
        <html>
        <head>
            <meta charset="utf-8" />
            <title>TypeScript HTML App</title>
            <link rel="stylesheet" href="app.css" type="text/css"/>
            <script src="Scripts/require.js"></script>
            <script src="Scripts/App/app.js"></script>
        </head>
        <body>
            <h1>TypeScript HTML App</h1>
        
            <div id="content"></div>
        </body>
        </html>
        و فایل tsconfig.json  رو به پروژه با محتوای زیر اضافه کردم
        {
            "compilerOptions": {
                 "target": "es5",
                 "outDir": "Scripts/App",
                  "moduleResolution": "node",
                  "module": "commonjs",
                 "sourceMap": true,
                 "experimentalDecorators": true,
                 "emitDecoratorMetadata": true
            }
        }

        مشکل کجاست به نظرتون ؟
        • #
          ‫۸ سال و ۳ ماه قبل، دوشنبه ۷ تیر ۱۳۹۵، ساعت ۰۲:۳۴
          نوع module رو در فایل tsconfig به amd تغییر دادم . حالا به جای خطاهای ذکر شده خطای زیر رو میگیرم در حالی که چشمی همه چیز درست به نظر میاد
          r.js:392 Uncaught Error: Mismatched anonymous define() module: function (require, exports, testmd_1) {
              "use strict";
              window.onload = function () {
                  var book = new testmd_1.Book();
                  alert(book.GetbookNmae);
                  document.getElementById("content").innerText = book.GetbookNmae;
              };
          }

        • #
          ‫۸ سال و ۳ ماه قبل، دوشنبه ۷ تیر ۱۳۹۵، ساعت ۰۲:۵۷
          - با هربار تغییر فایل tsconfig.json، کامپایل دوباره‌ی پروژه را فراموش نکنید (مهم). از منوی build گزینه‌ی rebuild solution را انتخاب کنید. این rebuild، کار کامپایل مجدد فایل‌های ts. را هم انجام می‌دهد.
          - commonjs بیشتر برای برنامه‌های nodejs استفاده می‌شود. اگر علاقمند باشید که با سیستمی شبیه به AngularJS 2.0 کار کنید، از یک module loader ویژه، به نام SystemJS استفاده کنید (که قابلیت بارگذاری خودکار ES6 modules, AMD, CommonJS را دارد). بنابراین فایل tsconfig.json را به این صورت تغییر دهید:
          {
              "compileOnSave": true,
              "compilerOptions": {
                  "target": "es5",
                  "module": "system",
                  "sourceMap": true
              }
          }
          بعد فایل index.html شما چنین شکلی را پیدا می‌کند:
          <!DOCTYPE html>
          <html>
          <head>
              <meta charset="utf-8" />
              <title>TypeScript HTML App</title>
              <link rel="stylesheet" href="app.css" type="text/css"/>
          
              <script type="text/javascript" src="//cdnjs.cloudflare.com/ajax/libs/systemjs/0.19.22/system.js"></script>
              <script type="text/javascript">
                  System.defaultJSExtensions = true;
                  System.import('app');
              </script>
          </head>
          <body>
              <h1>TypeScript HTML App</h1>
          
              <div id="content"></div>
          </body>
          </html>
          در اینجا System.JS کار بارگذاری اولین ماژول برنامه یا همان app.js را به صورت خودکار انجام می‌دهد (و همچنین تمام ماژول‌های مرتبط با آن‌را). بنابراین دیگر نیازی به ذکر اسکریپت‌های برنامه در اینجا نیست (هیچکدام از آن‌ها، منهای موارد عمومی مثل خود system.js).
          بعد فایل app.ts را هم به این صورت تغییر دهید، چون این کدها پس از onload اجرا می‌شوند:
          import {Book} from "./testmd";
          
          let book: Book = new Book();
          console.log(book.bookName);
          document.getElementById("content").innerText = book.GetbookNmae;
          • #
            ‫۸ سال و ۳ ماه قبل، پنجشنبه ۱۰ تیر ۱۳۹۵، ساعت ۰۴:۴۱
            من module رو روی system قرار دادم و همین کد بالا را نوشتم ولی با رسیدن به کد
            System.defaultJSExtensions = true;
            خطای
            System is not defined
            را دریافت میکنم.
            یکی از موارد دیگه اینکه وقتی داخل tsconfig از outFiile استفاده می‌کنم فقط مجبور به استفاده از ماژول system یا amd هستم.
            • #
              ‫۸ سال و ۳ ماه قبل، پنجشنبه ۱۰ تیر ۱۳۹۵، ساعت ۰۴:۵۰
              به مسیر زیر دسترسی دارید؟
              http://cdnjs.cloudflare.com/ajax/libs/systemjs/0.19.31/system.js
              و یا از این روش استفاده کنید برای دریافت فایل‌های آن:
              npm install systemjs
              • #
                ‫۸ سال و ۳ ماه قبل، پنجشنبه ۱۰ تیر ۱۳۹۵، ساعت ۰۶:۱۵
                خود لینک مسدود نبود ولی با نسخه مجود در node modules لینک کردم خطا رفع شد.
                با تشکر از شما
                • #
                  ‫۸ سال و ۳ ماه قبل، پنجشنبه ۱۰ تیر ۱۳۹۵، ساعت ۰۶:۳۳
                  اصلاح شده‌ی مثالی که پیوست کردند (امتحان شده): TS_Test.zip
                  • #
                    ‫۷ سال و ۱۱ ماه قبل، جمعه ۲۳ مهر ۱۳۹۵، ساعت ۲۳:۳۵
                    سلام 
                    من فایل اصلاح شده را دانلود کردم و بعد از اجرا با خطای زیر مواجه شدم
                    system.src.js:5123XMLHttpRequest cannot load file:///E:/MyDocument/TS_Test/app.js. Cross origin requests are only supported for protocol schemes: http, data, chrome, chrome-extension, https, chrome-extension-resource. 
                    • #
                      ‫۷ سال و ۱۱ ماه قبل، جمعه ۲۳ مهر ۱۳۹۵، ساعت ۲۳:۵۳
                      پروتکل //:file را تبدیل کنید به پروتکل //:http، کار می‌کند (یعنی همان پروژه‌ای را که پیوست شده، یکبار از طریق ویژوال استودیو اجرا کنید تا وب سرور آن برنامه را توزیع کند).