سال دوم کار حرفه‌ای‌ام، یک باگ عجیب در یک پروژه‌ی فروشگاهی مرا سه روز زمینگیر کرد. بررسی‌های اولیه هیچ خطایی نشان نمی‌داد: نه در کنسول مرورگر، نه در لاگ سرور، نه در تست‌های خودکار. مشکل این بود که در بخشی از کد، مقدار یک فیلد به‌جای عدد، به‌صورت رشته از API برمی‌گشت و ضرب شدنش در یک عدد، نتیجه‌ی NaN تولید می‌کرد. جاوااسکریپت هیچ هشداری نمی‌داد چون از نظر زبان، این کد کاملاً معتبر بود. اگر آن پروژه با TypeScript نوشته شده بود، همین یک باگ ساده در لحظه‌ی کامپایل گرفته می‌شد و سه روز از عمر تیم را نمی‌خورد. آن تجربه، نقطه‌ی چرخش من به سمت تایپ اسکریپت بود و از آن زمان، پروژه‌های جدی‌ام را از ابتدا با TS شروع می‌کنم.

TypeScript یک زبان برنامه‌نویسی است که به‌عنوان superset روی جاوااسکریپت ساخته شده: هر کد جاوااسکریپتِ معتبر، کد تایپ اسکریپتِ معتبر هم هست، اما TS امکانات اضافه‌ای می‌دهد که مهم‌ترینش سیستم تایپ ایستا (Static Type System) است. اگر در مسیر آموزش جاوااسکریپت از صفر هستید، این نوشته مرحله‌ی بعدی و طبیعی مسیر شماست؛ چون TS بدون تسلط بر JS، فقط یک لایه‌ی دست‌وپاگیر به‌نظر می‌رسد، اما با تسلط بر JS، یک ابرقدرت می‌شود.

چرا تایپ اسکریپت زاده شد؟

TypeScript در سال ۲۰۱۲ توسط مایکروسافت معرفی شد، اما دلیلش فقط «ساختن یک زبان جدید» نبود. مسئله‌ای که TS به آن پاسخ داد، یک دردِ واقعی در پروژه‌های بزرگ جاوااسکریپت بود: هر چه پروژه بزرگ‌تر می‌شود، نگهداری آن بدون یک سیستم تایپ قوی، سخت‌تر می‌شود. سه مشکل مشخص که در پروژه‌های JS مقیاس‌پذیر به‌طور مداوم دیده می‌شود:

  • باگ‌های پنهان در زمان اجرا: در JS، خیلی از اشتباه‌های تایپ فقط در زمان اجرا و در شرایط خاص خودش را نشان می‌دهند. TS این اشتباه‌ها را در زمان کامپایل و روی محیط توسعه می‌گیرد.
  • فقدان مستندسازی خودکار: در پروژه‌ی JS، فهمیدن اینکه یک تابع چه چیزی می‌گیرد و چه چیزی برمی‌گرداند، اغلب نیاز به مطالعه‌ی کد یا کامنت‌های دستی دارد. در TS، این اطلاعات در خود امضای تابع تعبیه شده است.
  • Refactoring پرخطر: تغییر یک نام یا تغییر ساختار یک شیء در پروژه‌های JS بزرگ، به یک کابوس تبدیل می‌شود چون هیچ ابزاری نمی‌تواند مطمئن باشد کدام قسمت‌های کد تحت تأثیر قرار می‌گیرند. در TS، کامپایلر به شما می‌گوید کجای کد باید اصلاح شود.

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

در پروژه‌های کوچک، تایپ اسکریپت یک اضافه است؛ در پروژه‌های بزرگ، یک زیرساخت اجتناب‌ناپذیر. تفاوت این دو نگاه، همان تفاوت بین یک اسکریپت و یک نرم‌افزار است.

تفاوت واقعی تایپ اسکریپت و جاوااسکریپت

یک تصور غلط رایج این است که TS و JS دو زبان مختلف هستند. در واقعیت، TS روی JS سوار است: هر کد JS، کد TS هم هست. تفاوت‌های بنیادی:

معیارJavaScriptTypeScript
سیستم تایپپویا (در زمان اجرا)ایستا (در زمان کامپایل)
اجرامستقیم در مرورگر یا Node.jsابتدا به JS کامپایل می‌شود
خطاهای تایپدر زمان اجرادر زمان کامپایل (قبل از اجرا)
ابزار و IDEپایهAuto-complete، Refactor و ناوبری پیشرفته
یادگیریساده‌تر برای شروعنیاز به تسلط بر JS اول
مناسب برایپروژه‌های کوچک، اسکریپت‌های سریعپروژه‌های بزرگ، تیمی، بلندمدت
خروجی نهاییهمان کدجاوااسکریپت معادل

نکته‌ی مهم: تایپ‌های TS در زمان اجرا وجود ندارند. یعنی وقتی کد TS شما به JS کامپایل شد، تمام اطلاعات تایپ حذف می‌شود و فقط منطق باقی می‌ماند. به همین دلیل، TS هیچ هزینه‌ی کارایی روی سایت شما ندارد — فقط یک لایه‌ی محافظتی در زمان توسعه است. مطالعه‌ی بیشتر در بهینه سازی جاوااسکریپت.

نصب و راه‌اندازی اولین پروژه

نصب TS ساده است و از طریق npm انجام می‌شود:

npm install -g typescript
tsc --version

یک فایل TypeScript با پسوند .ts بسازید و آن را با کامپایلر TS کامپایل کنید:

// greeting.ts
const user: string = "Ali";
console.log(`Hello, ${user}`);
tsc greeting.ts

خروجی یک فایل greeting.js خواهد بود. برای پروژه‌های واقعی، استفاده از tsc --init یک فایل tsconfig.json می‌سازد که تنظیمات کامپایلر را مدیریت می‌کند. در بخش tsconfig به آن می‌رسیم.

در پروژه‌های مدرن، معمولاً TS را با یک bundler مثل Vite یا Webpack ترکیب می‌کنیم. برای مطالعه‌ی موازی این لایه با اکوسیستم، یادگیری جاوااسکریپت با پروژه‌های واقعی و مفاهیم پیشرفته جاوااسکریپت دید وسیع‌تری می‌دهند.

تایپ‌های پایه در تایپ اسکریپت

TS مجموعه‌ای از تایپ‌های پایه دارد که در پروژه‌ها زیاد استفاده می‌شوند:

let isDone: boolean = false;
let count: number = 42;
let name: string = "Ali";
let list: number[] = [1, 2, 3];
let tuple: [string, number] = ["age", 30];
let notSure: unknown = "hello";
let nothing: null = null;
let notDefined: undefined = undefined;
let anything: any = "I can be anything";

سه نکته که در پروژه‌ها به‌کارم آمده:

  • any را جدی نگیرید: any عملاً تایپ‌چکینگ TS را خاموش می‌کند. اگر مجبور به استفاده از آن شدید، سعی کنید حداقل با کامنت توضیح دهید چرا. در پروژه‌ای که ۲۰۰ مورد any داشت، بعد از پاکسازی، چند باگ پنهان کشف شد.
  • unknown جایگزین امن‌تر any است: unknown هر مقداری را می‌پذیرد اما قبل از استفاده، شما را مجبور به بررسی تایپ می‌کند. این یک لایه‌ی اضافی از امنیت است.
  • Type Inference: در بسیاری از موارد، نیازی به نوشتن صریح تایپ نیست. TS خودش استنتاج می‌کند:
let count = 42;        // TS خودش می‌فهمد number است
let name = "Ali";      // TS خودش می‌فهمد string است
const list = [1, 2];   // TS خودش می‌فهمد number[] است

برای تایپ‌های پیشرفته‌تر مثل union، intersection و literal types، پیشنهاد می‌کنم بعد از تسلط بر تایپ‌های پایه، به سراغشان بروید. مطالعه‌ی بیشتر در تفاوت TypeScript و JavaScript.

interface و type: تفاوت و انتخاب درست

در TS دو روش برای تعریف شکل یک شیء وجود دارد: interface و type. تفاوت‌های ظریف اما مهمی دارند:

// interface
interface User {
  id: number;
  name: string;
  email?: string;  // اختیاری
}

// type
type User = {
  id: number;
  name: string;
  email?: string;
};

تفاوت‌های عملی:

معیارinterfacetype
تعریف شکل شیءبلهبله
تعریف Unionخیربله (type ID = string | number)
تعریف Tupleخیربله
گسترش (extend)با extendsبا intersection
Declaration Mergingبلهخیر
عملکرد در کامپایلرکمی سریع‌ترکمی انعطاف‌پذیرتر

قاعده‌ی من در پروژه‌ها: برای شکل اشیاء و کلاس‌ها، از interface استفاده کنید چون امکانات گسترش و ادغام دارد. برای Union، Tuple و تایپ‌های پیچیده، از type استفاده کنید. در پروژه‌ای که مخلوطی از هر دو استفاده شده بود، سردرگمی‌های زیادی ایجاد شد. یکی را انتخاب کنید و به آن پایبند بمانید.

یک نکته‌ی ظریف در تیم‌ها: interface در پروژه‌هایی که کتابخانه‌های خارجی استفاده می‌کنند، گاهی از نظر Declaration Merging مزیت دارد چون می‌توانید به interface تعریف‌شده در library چیزهای جدیدی اضافه کنید. اما در پروژه‌های داخلی، تفاوت عملی ناچیز است.

تایپ‌دهی به توابع و پارامترها

توابع، قلب هر پروژه‌ی TS هستند. تایپ‌دهی درست به توابع، بیشترین اثر را روی کیفیت کد دارد:

function greet(name: string, age?: number): string {
  return age ? `Hi ${name}, ${age} years old` : `Hi ${name}`;
}

// arrow function با تایپ
const add = (a: number, b: number): number => a + b;

// تابع با پارامتر پیش‌فرض
function multiply(a: number, b: number = 2): number {
  return a * b;
}

چند نکته‌ی مهم:

  • تایپ بازگشتی را صریح بنویسید: همیشه خوب است که تایپ بازگشتی تابع را صریح مشخص کنید. این کار باعث می‌شود کامپایلر خطاهای ناشی از تغییرات را بهتر بگیرد و مستندسازی خودکار بهتری داشته باشید.
  • پارامترهای اختیاری با ?: پارامتر اختیاری همیشه باید بعد از پارامترهای اجباری بیاید.
  • Rest parameters: برای توابعی که تعداد نامشخصی ورودی می‌گیرند: function sum(...nums: number[]): number.
  • Function Types: تایپ کل یک تابع را می‌توانید در متغیر قرار دهید: type Callback = (result: string) => void;.

در پروژه‌ای که یک داشبورد مدیریتی داشتیم، تایپ‌دهی دقیق به توابع و پارامترها باعث شد حدود ۳۰٪ از باگ‌های سابق در فاز توسعه گرفته شوند — باگ‌هایی که در JS فقط در محیط production ظاهر می‌شدند. مطالعه‌ی بیشتر در تفاوت TypeScript و JavaScript.

Generics: نیروی پنهان تایپ اسکریپت

Generics یکی از پرکاربردترین و در عین حال کمتر درک‌شده‌ترین ویژگی‌های TS است. ایده ساده است: به‌جای نوشتن یک تابع برای هر تایپ، یک تابع می‌نویسید که با هر تایپ کار می‌کند اما تایپ را حفظ می‌کند:

function identity(value: T): T {
  return value;
}

const num = identity<number>(42);      // num: number
const str = identity<string>("hello"); // str: string

یک مثال کاربردی‌تر:

function getProperty<T, K extends keyof T>(obj: T, key: K): T[K] {
  return obj[key];
}

const user = { id: 1, name: "Ali" };
const name = getProperty(user, "name"); // name: string
const id = getProperty(user, "id");     // id: number

سه کاربرد اصلی Generics در پروژه‌ها:

  1. توابع کمکی (Utility Functions): توابعی مثل map، filter و sort که روی هر آرایه‌ای کار می‌کنند.
  2. ساختارهای داده (Data Structures): پیاده‌سازی صف، پشته و درخت که با هر تایپ داده کار می‌کنند.
  3. API و درخواست‌های شبکه: تایپ‌دهی به پاسخ‌های API بدون تکرار برای هر endpoint.

یک مثال از پروژه‌ی خودم: در یک پروژه‌ی داشبورد، صدها درخواست API داشتیم که همه یک ساختار کلی داشتند (status، data، message). با Generics، یک تابع wrapper نوشتیم که پاسخ API را با تایپ دقیق برمی‌گرداند و در تمام پروژه استفاده شد. قبل از این wrapper، هر endpoint تایپ جداگانه داشت و نگهداری آن تقریباً غیرممکن بود.

Generics در TypeScript، مثل چاقوی همه‌کاره در دست یک آشپز حرفه‌ای است: در دست آماتور، گیج‌کننده و خطرناک؛ در دست حرفه‌ای، ابزاری برای ساخت هر چیزی.

tsconfig و تنظیمات مهم

فایل tsconfig.json قلب تنظیمات TS است. در پروژه‌ها، این تنظیمات بیشترین اثر را روی کیفیت کد و رفتار کامپایلر دارند:

{
  "compilerOptions": {
    "target": "ES2020",
    "module": "ESNext",
    "strict": true,
    "noImplicitAny": true,
    "strictNullChecks": true,
    "esModuleInterop": true,
    "skipLibCheck": true,
    "forceConsistentCasingInFileNames": true,
    "outDir": "./dist",
    "rootDir": "./src",
    "sourceMap": true
  },
  "include": ["src/**/*"],
  "exclude": ["node_modules", "dist"]
}

چهار تنظیم که در پروژه‌ها حتماً فعال می‌کنم:

  • strict: true: تمام تنظیمات سخت‌گیرانه را فعال می‌کند. در پروژه‌های جدید، این یک تصمیم غیرقابل‌مذاکره است.
  • noImplicitAny: true: جلوگیری از استفاده‌ی ضمنی از any. این یعنی اگر تایپی را مشخص نکنید، کامپایلر به شما هشدار می‌دهد.
  • strictNullChecks: true: باعث می‌شود null و undefined به‌عنوان تایپ‌های مستقل دیده شوند. این یکی از مؤثرترین تنظیمات در جلوگیری از باگ‌های null reference است.
  • esModuleInterop: true: سازگاری بهتر با ماژول‌های CommonJS و ESM. برای پروژه‌های مدرن ضروری است.

در پروژه‌ای که یک تیم فنی روی یک پروژه‌ی بزرگ کار می‌کرد، فعال‌سازی strict: true روی یک پروژه‌ی موجود (که ابتدا با تنظیمات پیش‌فرض شروع شده بود) حدود ۴۰۰ خطای تایپ را آشکار کرد. بسیاری از این خطاها باگ‌های بالقوه‌ای بودند که تا آن زمان پنهان مانده بودند. این یعنی یکی از آن موقعیت‌های نادری که سخت‌گیری TS، مستقیماً باگ‌های واقعی را کشف کرد.

مهاجرت تدریجی پروژه‌های JS به TS

یکی از پرتکرارترین سؤال‌هایی که در تیم‌ها می‌شنوم: «پروژه‌ی JS من بزرگ است، چطور به TS مهاجرت کنم؟» پاسخ صادقانه: مهاجرت کامل و یک‌شبه، تقریباً همیشه اشتباه است. مسیر درست، مهاجرت تدریجی است:

  1. نصب TS و افزودن tsconfig.json با تنظیمات آسان: ابتدا با strict: false و allowJs: true شروع کنید. یعنی TS را می‌پذیرید اما فایل‌های JS را هم قبول می‌کند.
  2. فایل‌های جدید را با TS بنویسید: از این پس، هر فایل جدید .ts خواهد بود. این یک قاعده‌ی ساده است که در طول زمان، به‌طور طبیعی پروژه را به سمت TS می‌برد.
  3. فایل‌های قدیمی را تدریجی مهاجرت دهید: ابتدا فایل‌های utility و helper، سپس کامپوننت‌ها، در آخر فایل‌های پیچیده.
  4. پس از مدتی، strict را فعال کنید: وقتی بخش عمده‌ی پروژه با TS نوشته شد، strict: true را فعال کنید و خطاها را تدریجی رفع کنید.
  5. در نهایت، allowJs را خاموش کنید: وقتی پروژه کاملاً TS شد، دیگر نیازی به اجازه‌ی فایل‌های JS نیست.

در پروژه‌ای که یک فروشگاه اینترنتی بزرگ بود، این مهاجرت تدریجی حدود چهار ماه طول کشید اما هر مرحله به‌تنهایی منتشر شد و کاربران چیزی متوجه نشدند. اگر همان مهاجرت را یک‌شبه انجام می‌دادیم، احتمالاً چند هفته سایت آفلاین می‌شد.

الگوهای واقعی از پروژه‌ها

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

  1. تایپ‌دهی به پاسخ API: برای هر endpoint، یک interface تعریف کنید و از تابع wrapper با Generic استفاده کنید. این الگو را در پروژه‌های داشبوردی زیاد استفاده کرده‌ام.
  2. استفاده از Type Guards: توابعی که تایپ را در runtime بررسی می‌کنند: function isString(value: unknown): value is string { return typeof value === "string"; }. این الگو در پردازش داده‌های ورودی نامشخص کاربرد زیادی دارد.
  3. Discriminated Unions: ترکیب union type با یک فیلد مشترک (مثل type) که به TS امکان می‌دهد بین حالت‌های مختلف تمایز قائل شود. در مدیریت state در برنامه‌های React و Vue، این یک الگوی استاندارد است.
type Result = 
  | { status: "success"; data: string }
  | { status: "error"; message: string };

function handle(result: Result) {
  if (result.status === "success") {
    console.log(result.data);    // TS می‌داند data وجود دارد
  } else {
    console.log(result.message); // TS می‌داند message وجود دارد
  }
}

برای مطالعه‌ی چیدمان این الگوها در پروژه‌های واقعی، یادگیری جاوااسکریپت با پروژه‌های واقعی و مفاهیم پیشرفته جاوااسکریپت دید وسیع‌تری می‌دهند.

اشتباهاتی که در پروژه‌ها دیدم

  • استفاده‌ی بی‌رویه از any: any تایپ‌چکینگ TS را در آن بخش خاموش می‌کند. اگر مجبور به استفاده از آن شدید، سعی کنید با unknown جایگزین کنید یا نوع دقیق را بنویسید.
  • استفاده از Type Assertion به‌جای Type Guard: as SomeType یک ادعاست، نه بررسی. Type Guard واقعی با value is Type رفتار امن‌تری دارد.
  • تعریف interface برای همه‌چیز: در بعضی پروژه‌ها، توسعه‌دهنده‌ها برای یک شیء که فقط در یک تابع استفاده می‌شود، interface جداگانه می‌سازند. TS یک گزینه‌ی inline هم دارد که ساده‌تر است.
  • نادیده گرفتن strict: true: در پروژه‌های جدید، فعال‌سازی strict از ابتدا باعث می‌شود هر باگ بالقوه در زمان کامپایل گرفته شود. غیرفعال کردن آن، در واقع استفاده از نیمی از قدرت TS است.
  • مقایسه‌ی اشتباه TS با JS: بعضی تیم‌ها از TS فقط به‌عنوان «JS با تایپ» استفاده می‌کنند و از امکانات پیشرفته مثل Generics و Discriminated Unions بی‌خبرند. TS پتانسیل بسیار بیشتری دارد.
  • نادیده گرفتن tsconfig: در بعضی پروژه‌ها، tsconfig پیش‌فرض استفاده می‌شود و تنظیمات بهینه اعمال نمی‌شود. صرف دو ساعت روی این فایل، در طول عمر پروژه چندین برابر برمی‌گردد.
  • نبود Type Declaration برای کتابخانه‌های خارجی: بعضی کتابخانه‌ها تایپ‌های آماده ندارند. راه‌حل: بسته‌های @types/... یا نوشتن یک .d.ts ساده برای آن‌ها.

بخشی از این اشتباهات در اشتباهات رایج توسعه و بهینه سازی جاوااسکریپت هم آمده است.

لایه‌ای پایین‌تر از سیستم تایپ

اینجا وارد لایه‌ای می‌شوم که در پروژه‌های معمولی به آن نگاه نمی‌شود اما برای مهندسان پلتفرم و توسعه‌دهنده‌های ارشد اهمیت دارد. آنچه TypeScript و کامپایلر آن با کد شما می‌کند، در پنج مفهوم خلاصه می‌شود:

  1. Structural Typing و Nominal Typing: TS از سیستم تایپ ساختاری (structural) استفاده می‌کند، یعنی تایپ‌ها بر اساس ساختارشان مقایسه می‌شوند نه نامشان. یک interface به اسم User و یک interface به اسم Person که ساختار یکسانی داشته باشند، در TS معادل در نظر گرفته می‌شوند. این تفاوت با زبان‌هایی مثل Java و C# که Nominal هستند، پیامدهای عملی زیادی دارد: هم انعطاف بیشتری می‌دهد، هم می‌تواند باعث خطاهای ظریف شود. مطالعه‌ی موازی این لایه در تفاوت TypeScript و JavaScript آمده است.
  2. Type Erasure و هزینه‌ی زمان اجرا: همه‌ی تایپ‌های TS در زمان کامپایل حذف می‌شوند. یعنی خروجی نهایی JS است و هیچ هزینه‌ی کارایی در runtime وجود ندارد. اما نکته‌ی ظریف اینجاست که اگر از Type Assertion (as Type) بیش‌ازحد استفاده کنید، این حذف تایپ می‌تواند به‌معنای حذف محافظت واقعی شود. یک as number روی مقداری که در واقع string است، در runtime به یک باگ غیرقابل‌پیش‌بینی تبدیل می‌شود. مطالعه‌ی این لایه در چارچوب کارایی در بهینه سازی جاوااسکریپت آمده است.
  3. Declaration Files و Interaction با کتابخانه‌ها: فایل‌های .d.ts که برای کتابخانه‌های JS موجود نوشته می‌شوند، یک لایه‌ی تایپ روی API آن کتابخانه هستند. این فایل‌ها می‌توانند از منبع اصلی (کتابخانه خودش) بیایند یا از پکیج‌های @types/.... اگر تایپ‌های یک کتابخانه اشتباه یا ناقص باشند، کامپایلر TS می‌تواند پیام‌های گمراه‌کننده بدهد. این یک لایه‌ی مهندسی است که در پروژه‌های بزرگ اهمیت دارد. مطالعه‌ی موازی این لایه با اکوسیستم در توسعه وردپرس از صفر و گیت در وردپرس آمده است.
  4. Type-Level Programming و Conditional Types: سیستم تایپ TS آن‌قدر قدرتمند است که می‌توان با آن «برنامه‌نویسی» کرد. Conditional Types، Mapped Types و Template Literal Types امکاناتی هستند که در کتابخانه‌های پیچیده مثل React و Prisma به‌طور سنگین استفاده می‌شوند. در پروژه‌های معمولی، نیازی به این سطح نیست اما در کتابخانه‌های عمومی، این ابزارها تفاوت بین یک کتابخانه‌ی معمولی و یک کتابخانه‌ی حرفه‌ای را می‌سازند. مطالعه‌ی موازی در مفاهیم پیشرفته جاوااسکریپت.
  5. Interaction با Bundlers و Build Pipeline: کامپایلر TS به‌تنهایی برای پروژه‌های مدرن کافی نیست. با Vite، Webpack یا esbuild ترکیب می‌شود که هرکدام رفتار متفاوتی دارند. مهم‌ترین تفاوت: بعضی bundlerها (مثل esbuild) تایپ‌چکینگ را نادیده می‌گیرند و فقط ترنسپایل می‌کنند. یعنی اگر build با esbuild اجرا شود، خطاهای تایپ در زمان build گرفته نمی‌شوند و باید جداگانه tsc --noEmit را در pipeline اجرا کنید. در پروژه‌ای که این نکته را نمی‌دانستیم، چند خطای تایپ در production پیدا شد که اگر tsc --noEmit در CI/CD اجرا می‌شد، گرفته می‌شد. مطالعه‌ی این لایه در ابزارهای CI/CD و توسعه وردپرس از صفر آمده است.

یک تجربه‌ی واقعی از پروژه‌ای که با مسئله‌ی Type Erasure روبرو شدیم: در یک پروژه‌ی React، برای کاهش زمان کامپایل از babel-plugin-transform-typescript استفاده می‌کردیم که به‌طور پیش‌فرض Type Assertion را نادیده می‌گیرد. نتیجه: در جایی که توسعه‌دهنده با as number یک مقدار را به عدد تبدیل کرده بود اما در واقعیت API مقدار رشته برمی‌گرداند، این خطا در runtime به یک NaN خاموش تبدیل شد که در داشبورد مدیریتی، جمع کل فروش را غلط نشان می‌داد. راه‌حل: انتقال به tsc برای کامپایل، که این ادعاها را حتی با Type Assertion بررسی می‌کند و در صورت شک، خطا می‌دهد.

اگر روی پروژه‌های وردپرسی هستید و می‌خواهید این لایه‌ها را در development pipeline خود اعمال کنید، پیشنهاد می‌کنم ابتدا به توسعه وردپرس از صفر نگاهی بیندازید. برای مطالعه‌ی موازی با معماری، استانداردهای HTML و CSS و CSS مدرن از Flexbox تا Grid دید وسیع‌تری می‌دهند. برای درک این لایه در چارچوب کارایی، بهینه سازی جاوااسکریپت، بهینه‌سازی سرعت سایت و ابزارهای CI/CD منابع کلیدی هستند. اگر روی موضوع تست و کیفیت کد متمرکز هستید، گیت در وردپرس و اشتباهات رایج توسعه را هم ببینید.

TypeScript یک زبان نیست که جاوااسکریپت را جایگزین کند؛ یک لایه‌ی محافظتی است که روی همان جاوااسکریپت، عمق و امنیت می‌آورد — و این عمق، تفاوت بین یک پروژه‌ی معمولی و یک محصول حرفه‌ای است.

نگاه آخر این مسیر

تایپ اسکریپت را می‌توان در یک جمله خلاصه کرد: «جاوااسکریپت با یک سیستم تایپ که در زمان کامپایل، به شما می‌گوید کدام بخش‌های کد مشکوک‌اند.» سه درس که از این مسیر با خودم بردم:

  1. قبل از TS، جاوااسکریپت را عمیق یاد بگیرید. TS بدون درک جاوااسکریپت، مثل رانندگی با ترمز دستی است. اگر هنوز با مفاهیمی مثل closure، prototype و async/await راحت نیستید، ابتدا آموزش جاوااسکریپت از صفر را کامل کنید و بعد به سراغ TS بیایید.
  2. strict: true را از روز اول فعال کنید. این یک تصمیم ساده است که در طول عمر پروژه، چندین برابر برمی‌گردد. اگر پروژه‌ی موجود دارید، مهاجرت تدریجی به strict، بهترین سرمایه‌گذاری ممکن روی کیفیت کد است.
  3. Generics را جدی بگیرید. اگر فقط در حد تایپ‌های پایه از TS استفاده کنید، نصف قدرت آن را هدر داده‌اید. Generics، Union Types و Type Guards ابزارهایی هستند که تفاوت بین «JS با تایپ» و «TS واقعی» را می‌سازند.

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

در تجربه‌ی خودم، بزرگ‌ترین دستاورد TypeScript همیشه اول در سطح تیم و پروژه دیده می‌شود، نه در سطح یک خط کد. اگر تجربه‌ای از مهاجرت به TS دارید — از آن‌هایی که کد را نجات داد، یا از آن‌هایی که پیچیدگی را بیشتر کرد — آن را با ما به اشتراک بگذارید. مهم‌تر از همه، اگر در پروژه‌ی خودتان به یک الگوی خاص TS رسیده‌اید که بهره‌وری را دوچندان کرده، آن تجربه برای نفر بعدی که همین مسیر را طی می‌کند، از هر مستند رسمی ارزشمندتر است.