تایپ اسکریپت قابلیت تایپ استاتیک را به جاوااسکریپت اضافه می‌کند؛ قابلیتی که به کاهش رفتارهای غیرقابل پیش‌بینی و خطاهای احتمالی کمک می‌کند. در گذشته، امکان اجرای مستقیم فایل‌های تایپ اسکریپت در Node.js وجود نداشت. روش مرسوم این بود که ابتدا کد تایپ اسکریپت با ابزار خط فرمان تایپ اسکریپت (tsc) به جاوااسکریپت کامپایل شود و سپس نسخه خروجی اجرا گردد.

برای اجرای مستقیم تایپ اسکریپت در Node.js باید از ابزارهای جانبی مانند ts-node یا tsx استفاده می‌شد. اما با انتشار نسخه 22.6 از Node.js، پشتیبانی سبک و آزمایشی از اجرای مستقیم تایپ اسکریپت به هسته اضافه شد. این پشتیبانی از طریق قابلیتی به نام type stripping فراهم شده است؛ قابلیتی که Node.js را عملاً به یک TypeScript runner تبدیل می‌کند.

چرا به TypeScript runner نیاز داریم؟ و آیا کامپایلر رسمی TypeScript به‌تنهایی پاسخ‌گوی نیازهای ما نیست؟ در این مقاله مزایای اجرای مستقیم تایپ اسکریپت در Node.js را تشریح می‌کنیم و رویکردهای مختلف موجود را مورد مقایسه قرار می‌دهیم.

چرا کامپایلر رسمی تایپ اسکریپت کافی نیست؟

کامپایلر رسمی تایپ اسکریپت وظایف مهمی را در یک پروژه برعهده دارد: بررسی خطاهای سینتکسی، اعتبارسنجی و استنتاج تایپ‌ها، پردازش تنظیمات موجود در فایل tsconfig.json و در نهایت تبدیل کد تایپ اسکریپت به جاوااسکریپت برای اجرا در یک محیط جاوااسکریپت.

از نظر فنی، این روند می‌تواند برای توسعه برنامه‌های تایپ اسکریپت کافی به نظر برسد، زیرا محیط‌هایی مانند مرورگر یا Node.js به‌صورت پیش‌فرض قادر به اجرای تایپ اسکریپت نیستند. اما در عمل، استفاده از یک TypeScript runner در زمان توسعه بسیار کارآمدتر است. در گذشته، نه Node.js و نه تایپ اسکریپت قابلیت اجرای مستقیم فایل‌های TS را بدون ابزار جانبی ارائه نمی‌کردند.

TypeScript runner به هر ابزاری گفته می‌شود که بتواند فایل‌های تایپ اسکریپت را بدون نیاز به مرحله کامپایل جداگانه اجرا کند. محیط‌هایی مثل Bun و Deno این قابلیت را به‌صورت داخلی فراهم کرده‌اند. این ابزارها امکان اجرای مستقیم فایل‌های TS را تنها در یک مرحله، بدون کامپایل و اجرای جداگانه، فراهم می‌کنند.

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

برخی runnerها امکاناتی مانند حالت مشاهده تغییرات (watch mode)، محیط REPL و حتی قابلیت type checking را نیز ارائه می‌دهند.

در ادامه، سه روش اجرای تایپ اسکریپت در Node.js بررسی می‌شود:

در هر بخش، ویژگی‌ها، مزایا و محدودیت‌های هر ابزار را بررسی خواهیم کرد تا توسعه‌دهندگان بتوانند انتخاب مناسب‌تری داشته باشند.

Type Stripping در Node.js

Node.js در نسخه 22.6، فلگ جدیدی با نام --experimental-strip-types معرفی کرد. با فعال‌سازی این فلگ، Node.js پیش از اجرا، فایل تایپ اسکریپت را با روش type stripping به جاوااسکریپت تبدیل می‌کند.

Type stripping فرآیندی است که طی آن تمام تایپ‌ها و سینتکس‌های مخصوص تایپ اسکریپت حذف می‌شوند. اگر فایل تایپ اسکریپت تنها شامل سینتکس‌های قابل حذف باشد، این فرآیند آن را به‌طور خودکار به جاوااسکریپت معتبر تبدیل می‌کند.

«سینتکس قابل حذف» به بخش‌هایی از کد گفته می‌شود که فقط متعلق به تایپ اسکریپت هستند و در زمان اجرا هیچ خروجی یا اثری ایجاد نمی‌کنند. Node.js این فرایند را با کمک پکیج amaro انجام می‌دهد؛ ابزاری سبک وزن که سینتکس‌های قابل حذف را با فضای خالی جایگزین می‌کند (مشابه ابزار ts-blank-space).

مزیت این روش این است که معمولاً نیازی به ساخت source map وجود ندارد، زیرا موقعیت خطوط تغییر نمی‌کند و خطاها در همان خطوط اصلی گزارش می‌شوند.

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

در نسخه 22.7، فلگ جدیدی معرفی شد: --experimental-transform-types .

این فلگ علاوه‌بر حذف تایپ‌ها، می‌تواند بخش‌هایی از تایپ اسکریپت که قابل حذف نیستند را نیز به جاوااسکریپت تبدیل کند. با این حال، این فرایند نیازمند ساخت source map است و در نتیجه نسبت به type stripping سنگین‌تر و کندتر محسوب می‌شود.

نکته مهم این است که Node.js تنها سینتکس‌های قابل حذف را مدیریت می‌کند و هیچ‌گونه type checking انجام نمی‌دهد. مسئولیت بررسی تایپ‌ها همچنان برعهده توسعه‌دهنده است. البته در جریان توسعه، معمولاً خود IDE خطاهای مرتبط با تایپ یا سینتکس را گزارش می‌دهد. همچنین می‌توان بررسی تایپ‌ها را در مرحله lint، قبل از ارسال تغییرات به repository یا پیش از انتشار نهایی در محیط تولید انجام داد.

معایب استفاده از Type Stripping در Node.js

پیش از استفاده از قابلیت جدید Type Stripping در Node.js، لازم است چند نکته مهم را در نظر بگیریم:

پیکربندی تایپ اسکریپت برای Type Stripping در Node.js

با اینکه Node.js برای اجرای تایپ اسکریپت به فایل tsconfig.json وابسته نیست و Type Checking انجام نمی‌دهد، همچنان توصیه می‌شود پیش از انتشار کد، عملیات Type Checking انجام شود.

برای تکمیل این قابلیت، تایپ اسکریپت گزینه جدیدی با نام erasableSyntaxOnly در tsconfig و یک فلگ CLI با همین نام معرفی کرده است.

// tsconfig.json
{
  "compilerOptions": {
    // ...
    erasableSyntaxOnly: true,
  }
}

erasableSyntaxOnly چیست؟

زمانی که این گزینه فعال باشد، چه از طریق tsconfig.json و چه با فلگ CLI، کامپایلر تایپ اسکریپت هر زمان با سینتکس غیر قابل حذف مواجه شود، خطا تولید می‌کند.

به این ترتیب، توسعه‌دهنده مطمئن خواهد بود که هر فایلی که تایپ اسکریپت بدون خطا کامپایل می‌کند، در Node.js نیز قابل اجرا است.

این قابلیت برای ویرایشگرهای کد اهمیت زیادی دارد، چون بسیاری از ابزارها و پلاگین‌ها از tsconfig.json برای گزارش خطاها و هشدارها استفاده می‌کنند.

نحوه استفاده از Type Stripping در Node.js

در این بخش مراحل استفاده از Type Stripping را به‌صورت گام‌به‌گام بررسی می‌کنیم.

پیش از شروع کار، لازم است:

مراحل:

۱) ایجاد فایل tsconfig.json

با اینکه Node.js به tsconfig.json وابسته نیست، بهتر است برای یکپارچگی ابزارها حداقل تنظیمات لازم را در این فایل قرار دهیم. این پیکربندی رفتار تایپ اسکریپت را در نزدیک‌ترین حالت به اجرای واقعی Node.js قرار می‌دهد:

// tsconfig.json
{
  // ...
  "compilerOptions": {
    "noEmit": true,
    "target": "esnext",
    "module": "nodenext",
    "rewriteRelativeImportExtensions": true,
    "erasableSyntaxOnly": true,
    "verbatimModuleSyntax": true
  }
}

۲) نصب تایپ‌های Node.js

برای داشتن Type Safety هنگام کار با ماژول‌ها و APIهای Node.js باید تایپ‌ها را نصب کنیم:

npm install --save-dev @types/node

۳) نوشتن کد تایپ اسکریپت

یک فایل تایپ اسکریپت جدید ایجاد می‌کنیم:

// index.ts
function add(a: number, b: number) {
  return a + b;
}

console.log(add(2, 2));

۴) اجرای کد

کد را با دستور زیر اجرا می‌کنیم:

node index.ts # If using Node.js v23.6 or more
node --experimental-strip-types index.ts # If using Node.js v22.6 or more

خروجی این دستور عدد ۴ خواهد بود.

می‌توانیم برای سرعت توسعه از Type Stripping استفاده کنیم و هم‌زمان یک اسکریپت جدا برای Type Checking یا کامپایل نهایی، مثلاً هنگام Push به ریپازیتوری یا هنگام Deployment، در نظر بگیریم.

برای این کار، ابتدا باید تایپ اسکریپت رسمی را نصب کنیم:

npm install --save-dev typescript

سپس اسکریپت‌های package.json را طوری تنظیم می‌کنیم که در حالت توسعه از watch mode در Node.js استفاده شود و یک اسکریپت جدا نیز برای Type Checking داشته باشیم.

// package.json
{
  // ...
    "scripts": {
      "dev": "node --watch index.ts",
      "typecheck": "tsc"
  },
}

چه زمانی از Type Stripping در Node.js استفاده کنیم؟

بهترین کاربرد Type Stripping افزایش سرعت اجرای کد در مرحله توسعه است. با این حال، از آن‌جایی که این قابلیت در زمان نگارش این مقاله هنوز ناپایدار است، استفاده از آن در محیط Production توصیه نمی‌شود.

همچنین امکان انتشار یک پکیج npm به‌صورت مستقیم با تایپ اسکریپت وجود ندارد؛ بنابراین پس از انجام Type Checking با tsc باید کد را با ابزاری مانند swc یا esbuild فوراً به جاوااسکریپت تبدیل کرد.

ماژول ts-node

ts-node یک ماژول third-party در npm است که امکان اجرای مستقیم فایل‌های تایپ اسکریپت در Node.js را فراهم می‌کند. این ابزار یک REPL مخصوص تایپ اسکریپت نیز ارائه می‌دهد.

برخلاف Type Stripping که یک اجرای سبک از تایپ اسکریپت ارائه می‌دهد، ts-node از تمام قابلیت‌های تایپ اسکریپت پشتیبانی می‌کند.

به‌طور پیش‌فرض نیز عملیات Type Checking را انجام می‌دهد، مگر این‌که توسعه‌دهنده فلگ --transpileOnly را فعال کرده باشد.

فرآیند کامپایل در ts-node از طریق فایل tsconfig.json قابل پیکربندی است. همچنین این ابزار دارای یک اکوسیستم پلاگین و پشتیبانی از transpilerهای third-party مانند swc است. به همین دلیل می‌توان هم برای Pre-Compile کردن کد از آن استفاده کرد و هم در صورت نیاز، مستقیماً کد تایپ اسکریپت را در محیط Production اجرا نمود.

نحوه استفاده از ts-node

برای استفاده از ts-node در یک پروژه Node.js، مراحل زیر را طی می‌کنیم:

۱) نصب ts-node

در ابتدا باید ts-node را به‌عنوان یک وابستگی توسعه‌ای نصب کنیم. همچنین typescript باید جداگانه نصب شود، زیرا یک peer dependency برای ts-node محسوب می‌شود:

npm install -D ts-node typescript

۲) اجرای فایل تایپ اسکریپت

فرض می‌کنیم یک فایل تایپ اسکریپت با نام index.ts مطابق مثال‌های بخش قبلی وجود دارد. برای اجرای آن در Node.js، دستور زیر را اجرا می‌کنیم:

npx ts-node index.js # If module type is common js
npx node --loader ts-node/esm index.ts # If using ESM module type

با اجرای این دستور، فایل تایپ اسکریپت بدون نیاز به build شدن، مستقیماً اجرا می‌شود.

۳) تنظیم اسکریپت‌ها در package.json

می‌توانیم برای Type Checking و Transpile اسکریپت‌های جداگانه تعریف کنیم، مانند:

{
  // ..
  "type": "module",
  "scripts": {
    "dev": "node --watch --loader ts-node/esm index.ts",
    "typecheck": "tsc",
  },
}

۴) تنظیم tsconfig برای ts-node

در فایل tsconfig.json می‌توانیم بخش اختصاصی مربوط به "ts-node" را پیکربندی کنیم:

{
  // ...
  "ts-node": {
    "transpileOnly": true
  }
}

ts-node دارای گزینه‌ها و فلگ‌های مختلفی است که در مستندات رسمی آن توضیح داده شده‌اند.

با این پیکربندی، دستور npm run dev فایل تایپ اسکریپت را با سرعت بالا و بدون Type Checking اجرا می‌کند.

آیا می‌توان از ts-node در محیط Production استفاده کرد؟

ts-node توانایی Type Checking و اجرای مستقیم فایل‌های تایپ اسکریپت را دارد. در مرحله توسعه معمولاً نیازی نیست Type Checking را با ts-node انجام دهیم، چون ادیتورها خطاهای تایپی را نمایش می‌دهند. اما پیش از Deployment انجام Type Checking ضروری است.

اجرای پروژه Production با ts-node:

اگر پروژه مبتنی بر CommonJS باشد، اجرای ts-node در Production فقط زمانی منطقی است که:

معایب ts-node

با وجود قابلیت‌های فراوان ts-node، چند محدودیت مهم دارد:

چه زمانی از ts-node استفاده کنیم؟

بهترین سناریو برای استفاده از ts-node، حالت transpile-only است. این حالت سرعت اجرای کد را در مرحله توسعه به‌شدت افزایش می‌دهد و تجربه روان‌تری نسبت به سایر روش‌ها ارائه می‌دهد.

ماژول tsx

پکیج tsx (که نباید با پسوند فایل .tsx اشتباه گرفته شود)، خود را یک «ارتقای Node.js» معرفی می‌کند. این ابزار نیز مانند ts-node یک پکیج third-party است که امکان اجرای مستقیم تایپ اسکریپت در Node.js را فراهم می‌کند.

tsx با تمرکز بر تجربه کاربری ساده طراحی شده و برای افراد مبتدی گزینه‌ای بسیار مناسب‌تر محسوب می‌شود. پس از نصب، tsx عملاً نقش یک alias برای دستور node را ایفا می‌کند؛ یعنی تمام فلگ‌ها و آرگومان‌های CLI در Node.js را می‌پذیرد، اما علاوه بر آن قابلیت اجرای فایل‌های تایپ اسکریپت را نیز ارائه می‌دهد.

ویژگی‌های مهم tsx:

نحوه استفاده از tsx

برای استفاده از tsx، ابتدا باید آن را به‌عنوان یک dependency توسعه در پروژه نصب کنیم:

npm install -D tsx

اگرچه tsx بدون tsconfig کار می‌کند، اما سازندگان آن توصیه می‌کنند که یک فایل تنظیمات تایپ اسکریپت در پروژه وجود داشته باشد. یک پیکربندی پیشنهادی می‌تواند به شکل زیر باشد:

// tsconfig.json

{
  // ...
  "compilerOptions": {
    "moduleDetection": "force",
    "module": "Preserve",
    "resolveJsonModule": true,
    "allowJs": true,
    "esModuleInterop": true,
    "isolatedModules": true,
  }
}

پس از آن، می‌توانیم با استفاده از tsx، دقیقاً مشابه دستور node CLI، یک فایل را اجرا کنیم:

npx tsx index.js

یا اینکه اسکریپت‌های لازم را داخل فایل package.json تنظیم نماییم:

// package.json
{
  // ...
  "scripts": {
    "dev": "tsx index.ts"
  }
}

آیا می‌توان از tsx در محیط Production استفاده کرد؟

tsx از esbuild برای تبدیل تایپ اسکریپت استفاده می‌کند و همین موضوع آن را به گزینه‌ای مناسب برای Production تبدیل می‌کند. با این حال، اگر قصد استفاده از آن در محیط واقعی را داریم، باید توجه کنیم که tsx عملیات Type Checking را انجام نمی‌دهد. بنابراین بررسی تایپ‌ها باید با استفاده از tsc یا ابزارهای مشابه به‌صورت جداگانه صورت گیرد.

در مجموع، مشابه ts-node، استفاده از tsx در Production زمانی مناسب است که قصد نداشته باشیم سربار اضافی در مرحله اجرا ایجاد کنیم.

معایب tsx

با وجود فعال بودن توسعه و پشتیبانی عالی، tsx یک نقطه ضعف کوچک دارد:

جمع‌بندی

در این مقاله، روش‌های مختلف اجرای مستقیم تایپ اسکریپت در Node.js بررسی شد. ابتدا توضیح دادیم که چرا کامپایلر تایپ اسکریپت همیشه به‌تنهایی کافی نیست و سپس قابلیت Type Stripping در Node.js، نحوه استفاده و محدودیت‌های آن را بررسی کردیم.

در ادامه، دو ابزار محبوب ts-node و tsx معرفی شدند؛ ابزارهایی که امکان اجرای مستقیم فایل‌های تایپ اسکریپت، همراه با یک REPL اختصاصی، را فراهم می‌کنند. روش استفاده از این ابزارها و همچنین نقاط ضعف احتمالی هرکدام را در بخش‌های قبلی بررسی کردیم.

در نهایت، اضافه شدن قابلیت Type Stripping در Node.js باعث شده توسعه‌دهندگان در بسیاری از پروژه‌ها بتوانند بدون نیاز به ابزارهای جانبی، تایپ اسکریپت را اجرا کنند. با توسعه قابلیت experimental-transform-types، Node.js به‌تدریج به محیطی تبدیل می‌شود که مانند Deno یا Bun امکان اجرای کامل تایپ اسکریپت را فراهم می‌کند.