تایپ اسکریپت چیست؟ مفاهیم اولیه TypeScript

مفاهیم اولیه تایپ اسکریپت

تایپ اسکریپت یه زبان برنامه نویسی متن بازه که بر روی جاوا اسکریپت ساخته شده و قابلیت بررسی نوع، استاتیک به اون اضافه کرده. این کار باعث شده تا تایپ اسکریپت یه ابزار عالی برای کدنویسی در مقیاس های مختلف باشه.

توی این مطلب با مفاهیم اولیه و مهم تایپ اسکریپت آشنا خواهیم شد.

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

هر مقدار یا value توی جاوا اسکریپت دارای یه سری رفتار ها و ویژگی هاست که موقع اجرای عملیات مختلف روی اون مقدار، میشه این رفتار ها رو مشاهده کرد. بذارید با یه مثال توضیح بدم، متغیر message رو در نظر بگیرید که این عملیات رو روش انجام میدیم:

// دسترسی به یه ویژگی و فراخوانی اون ویژگی
message.toString();

// فراخوانی به عنوان یه تابع
message();

اینجا اولین خط کد که اجرا میشه اول به property یا ویژگی toString دسترسی پیدا می کنه و بعدش این ویژگی رو فراخوانی می کنه. خط دوم هم مستقیما خود message رو فراخوانی می کنه.

اگه فرض کنیم مقدار message رو نمی دونیم، با اطمینان نمی تونیم بگیم اجرای این کد چه نتیجه ای داره. نتیجه کاملا بستگی به مقدار message داره که ما ازش خبر نداریم و این سوالات پیش میاد:

  • آیا اصلا message قابل فراخوانیه؟

  • آیا ویژگی ای به اسم toString داره؟

  • اگه داره آیا toString هم قابل فراخوانیه؟

  • مقدار خروجی هر کدوم چی هست؟

مثلا اگه متغیر اینطوری تعریف شده باشه const message = "Hello" خط اول درست اجرا میشه ولی خط دوم یعنی message() خطا میده که رشته رو نمیشه مثل یه تابع فراخوانی کرد. وقتی کد جاوا اسکریپت می نویسیم معمولا تلاش می کنیم پاسخ این سوالات رو با جزئیات کامل توی ذهنمون نگه داریم.

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

در مقابل بررسی نوع پویا، سیستم بررسی نوع ایستا (static) هم وجود داره. بررسی نوع به صورت ایستا یا استاتیک یعنی بتونیم قبل از اجرای کد نتیجه رو پیش بینی کنیم.

بررسی نوع استاتیک یا ایستا

همونطور که گفتم ما معمولا تلاش می کنیم نوع ها رو با جزئیات کامل توی ذهنمون نگه داریم و اشتباه نکنیم چون در غیر این صورت باگ ها و رفتار های پیش بینی نشده زیادی رخ میدن. طبیعتا هم با بزرگتر شده پروژه احتمال خطای انسانی میره بالا و از کنترل خارج میشه.

تایپ اسکریپت با اضافه کردن یه سیستم بررسی نوع استاتیک (static type system) می تونه قبل از اجرا، خطا ها و باگ ها رو در لحظه نوشتن کد به ما نشون بده. همچنین اطلاعات مربوط به نوع مقادیر، تجربه کدنویسی رو میتونه بهتر کنه. مثلا قابلیت تکمیل خودکار کد جاوا اسکریپت توی VS Code با استفاده از تایپ اسکریپت کار می کنه.

const message = "hello!";
message(); // ⚠ خطا

اگه کد بالا رو توی تایپ اسکریپت بنویسیم میتونه بهمون اخطار بده که message قابل فراخوانی نیست. یعنی قبل از اجرای این کد متوجه ایرادش شدیم.

اشتباهات بدون خطا در زمان اجرا

تا اینجا فقط از چیزایی صحبت کردیم که موقع runtime یا همون اجرای کد، خطا میدن. مثلا اگه یه مقدار رو توی جاوا اسکریپت فراخوانی کنیم که تابع نباشه، خطا میده. ولی خیلی از مواقع هم درست همون لحظه اجرا خطایی داده نمیشه، با وجود اینکه کد اشتباهه یا حداقل اون چیزی که ما انتظارشو داریم نیست. این حالت هم معمولا منجر به باگ های زیادی میشه.

const user = {
  name: "Reza",
};

// این کد موقع اجرا خطایی نمیده
if (user.role !== 'admin') {
  ...
}

توی مثال بالا فرض کنین فراموش کردیم ویژگی role رو توی آبجکت user تعریف کنیم ولی موقع دسترسی به این ویژگی (داخل شرط if) بجای اینکه جاوا اسکریپت خطا بده، مقدار undefined رو بر می گردونه و کد اجرا میشه. ما هم چون خطایی موقع اجرا نمیده، به این راحتی متوجه اشتباهمون نمیشیم.

حالا اینکه چرا خطا نمیده و کلا همه رفتار های جاوا اسکریپت توی ECMAScript تعریف میشن که کاری باهاش نداریم. چون این حالت هم منجربه باگ های زیادی میشه تایپ اسکریپت به ما اخطار میده که بتونیم از این باگ ها جلوگیری کنیم.

نوع برای بهبود تجربه کدنویسی

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

تکمیل خودکار کد در تایپ اسکریپت

محیط کدنویسی و ادیتوری که از تایپ اسکریپت پشتیبانی میکنه میتونه علاوه بر تکمیل کد، کار هایی مثل اصلاح سریع (quick fix)، بازنویسی (refactoring)، پیدا کردن همه رفرنس ها و … هم انجام بده و در کل تجربه کدنویسی رو خیلی بهتر کنه. (ادیتور های پشتیبانی شده)

کامپایلر tsc تایپ اسکریپت

تایپ اسکریپت مستقیما توی مرورگر یا نود جی اس اجرا نمیشه و باید اول کامپایل و تبدیل به کد جاوا اسکریپت بشه. برای اینکار از کامپایلر تایپ اسکریپت یا همون tsc استفاده میکنیم. اول باید از طریق npm نصبش کنیم:

npm install -g typescript

دستور بالا پکیج کامپایلر تایپ اسکریپت رو برای کل سیستم نصب می کنه. اگه آپشن -g رو حذف کنید این پکیج فقط داخل پوشه و پروژه ای که هستید نصب میشه. مزیت نصب روی کل سیستم اینه که دستور tsc رو میشه تو کل سیستم اجرا کرد نه فقط همین پروژه.

اینجا من تصورم اینه که شما با NodeJS و اجرای دستورات و نصب پکیج های npm آشنایی دارین.

حالا داخل پوشه ای که هستین یه فایل تایپ اسکریپت مثلا hello.ts ایجاد و داخلش این کد رو اضافه کنین:

console.log("hello world");

با اجرای دستور tsc hello.ts این فایل کامپایل میشه و فایل hello.js حاوی کد جاوا اسکریپت کنارش ایجاد میشه. حالا اگه کدی بنویسیم که حاوی خطا باشه با اجرای این دستور موقع کامپایل، خطا رو به ما نشون میده.

function logger (message, date) {
  console.log(`${message}. logged at: ${date}`);
}

logger("hello"); // ⚠ خطا

تابع بالا دوتا ورودی داره درحالی که ما موقع فراخوانی فقط یه ورودی دادیم، با اجرای tsc hello.ts تایپ اسکریپت این خطا رو به ما نشون میده.

کامپایل با وجود خطا

اگه دقت کرده باشین توی مثال قبلی وقتی دستور tsc hello.ts رو اجرا کردیم با وجود اینکه خطا داشتیم ولی کامپایل موفقیت آمیز بود و فایل hello.js متناظر تولید شد.

وقتی از یه سیستم بررسی نوع استفاده میکنیم طبیعتا یه سری محدودیت ها موقع کدنویسی پیش میاد. ولی تایپ اسکریپت به طور پیشفرض سعی میکنه تا جای ممکن سد راه برنامه نویس نشه.

مثلا فرض کنین دارین یه پروژه رو از جاوا اسکریپت به تایپ اسکریپت منتقل می کنین. تایپ اسکریپت این اجازه رو میده که کد های قدیمی که درست کار می کنن فعلا همونطور باقی بمونن و کامپایل بشن و به مرور زمان بتونین کل پروژه رو تبدیل کنین.

یا یه جاهایی ما اطمینان داریم که کد درسته ولی کامپایلر به هر دلیلی اطلاعات لازم رو نداره و خطا میگیره. تایپ اسکریپت این جور مواقع میگه برنامه نویس بهتر میدونه و اگه بخواد میتونه خطا های منو نادیده بگیره.

البته اگه خواستین بیشتر جلوی باگ ها رو بگیرین کامپایلر رو تنظیم کنین که سختگیرانه تر عمل کنه و در صورت خطا کامپایل نشه. برای اینکار آپشن noEmitOnError رو به دستور اضافه کنین:

tsc --noEmitOnError hello.ts

اینطوری دیگه فایل hello.js تولید یا بروز نمیشه.

تعریف کردن نوع ها

ما تو مثال آخر به تایپ اسکریپت نگفتیم که نوع message و date تو ورودی تابع چی هستن. اگه بخوایم نوع message یه رشته (string) باشه و نوع date یه آبجکت Date باشه، به این شکل تابع رو تغییر میدیم:

function logger (message: string, date: Date) {
   ...
}

حالا اگه مثلا موقع فراخوانی این تابع ورودی اول رو عدد بدیم تایپ اسکریپت این اشتباه رو به ما میگه. یا مثلا یه اشتباه محتمل تر به این صورت:

logger("hello", Date()); // ⚠ خطا

کد بالا خطا میده چون نوع پارامتر دوم Date نیست. فراخوانی Date() در واقع یه رشته بر میگردونه. برای آبجکت Date باید مینوشتیم new Date() (با new). یعنی:

logger("hello", new Date());

البته همیشه هم لازم نیست نوع ها رو صراحتا بنویسیم و گاهی اوقات تایپ اسکریپت اون چیزی که مدنظر ما هست رو خودش تشخیص میده:

let name = "Reza";

name = 5; // ⚠ خطا نوع این متغیر رشته هست ولی عدد داده شده

اینجا نوع متغیر name از روی مقدار اولیه اش که "Reza" هست، string تشخیص داده شده و وقتی مقدارش رو به یه عدد تغییر میدیم خطا میده.

حذف نوع ها

آخرین تغییرات فایل hello.ts اینطوری شد:

function logger (message: string, date: Date) {
   console.log(`${message}. logged at: ${date}`);
}
logger("hello", new Date());

بیاین کد جاوا اسکریپتی که تولید شده رو بررسی و مقایسه کنیم:

function logger(message, date) {
    console.log("".concat(message, ". logged at: ").concat(date));
}
logger("hello", new Date());

توی کد جاوا اسکریپت تولید شده دو تا تفاوت بزرگ میبینیم:

  1. نوع های string و Date که برای پارامتر های ورودی نوشته بودیم حذف شدند.

  2. کد `${message}. logged at: ${date}` به یه روش دیگه نوشته شده.

نکته دوم رو یکم دیگه بررسی میکنیم فعلا روی نکته اول تمرکز کنیم. توی ساختار و syntax جاوا اسکریپت چنین کدی که برای تعریف نوع ها نوشتیم معتبر نیست. مرورگر یا هر محیطی که جاوا اسکریپت رو اجرا میکنه چنین تعاریفی رو نمیشناسه. اصلا به همین دلیله که تایپ اسکریپت نیاز به مرحله کامپایل شدن داره. کامپایلر میاد هر کدی که مختص نوع هاست و توی جاوا اسکریپت وجود نداره رو حذف میکنه. این کد ها و تعاریف فقط برای سیستم بررسی نوع کاربرد دارن و روی اجرای کد تاثیری ندارن، پس حذفشون روی عملکرد کد هیچ تاثیری نداره.

کاهش نسخه جاوا اسکریپت

و اما نکته دوم این بود که کد نوشته شده با Template string یعنی این کد:

`${message}. logged at: ${date}`

تبدیل شده به این کد:

"".concat(message, ". logged at: ").concat(date)

هر دو کد یه نتیجه دارن ولی یه تفاوتی وجود داره. روش اول با Template string نوشته شده که این قابلیت اخیرا به جاوا اسکریپت اضافه شده. این یعنی نسخه های قدیمی تر مرورگر ها از این قابلیت پشتیبانی نمی کنن.

تایپ اسکریپت سعی می کنه با کاهش ورژن و نسخه کد (یا همون ECMAScript) از مرورگر های بیشتری پشتیبانی کنه. حتی میشه قابلیت های جدید جاوا اسکریپت که تصویب شده ولی هنوز به مرورگر ها نیومده رو هم توی تایپ اسکریپت استفاده کرد و کامپایلر تبدیلش میکنه به نسخه پایین تر و قابل اجرا روی مرورگر.

البته ما میتونیم نسخه جاوا اسکریپت هدفمون رو با اضافه کردن آپشن target مشخص کنیم. هم از طریق فایل TSConfig که بعدا راجع بهش توضیح خواهم داد و هم از طریق خود دستور tsc می تونیم اینکار رو انجام بدیم:

tsc --target es2015 hello.ts

نسخه پیش فرض هم ES3 هست و لیست مقادیر ممکن رو اینجا می تونید ببینید.

میزان سخت گیری

توی تایپ اسکریپت چندین آپشن برای تنظیم میزان سخت گیری وجود داره. به صورت پیش فرض سخت گیری تایپ اسکریپت حداقله تا سد راه برنامه نویس نشه. مثلا برای تبدیل یه پروژه جاوا اسکریپتی به تایپ اسکریپت، این حالت ترجیح داده میشه.

ولی وقتی یه پروژه جدید میخوایم با تایپ اسکریپت کار کنیم می تونیم سخت گیری بیشتری اعمال کنیم تا با باگ های کمتری مواجه بشیم. آپشن strict همزمان همه این تنظیمات رو فعال یا غیرفعال میکنه. البته تک به تک هم میشه تنظیمات مربوط به سخت گیری رو فعال یا غیرفعال کرد. دو تا از مهم ترینشون رو باهم بررسی میکنیم:

تنظیم noImplicitAny

بعضی وقتا که نوع یک مقدار رو مشخص نمی کنیم، تایپ اسکریپت برای اون مقدار نوع any رو در نظر میگیره. نوع any یعنی هیچ بررسی و محدودیت روی این مقدار اعمال نکن و در واقع میشه همون جاوا اسکریپت خالی. مثلا:

function fn(s) {
  // پارامتر تابع رو any در نظر میگیره
  // و هیچ اخطاری به ما نمیده
  console.log(s.subtr(3));
}
fn(42);

اگه تنظیم noImplicitAny رو فعال کنیم، بجای اینکه نوع این پارامتر رو any تشخیص بده، خطا میده که نوع این مقدار باید مشخص بشه.

تنظیم strictNullChecks

به صورت پیش فرض نوع مقادیر null و undefined توی تایپ اسکریپت نادیده گرفته میشن. مثلا این آرایه رو در نظر بگیرین:

const users = [
  { "id": 123, "name": "Reza" },
  { "id": 456, "name": "Ali" },
  { "id": 789, "name": "Hesam" },
];

اگه با استفاده از find بخوایم یه آیتم از این آرایه پیدا کنیم، هیچ تضمینی وجود نداره که حتما این آیتم پیدا بشه و ممکنه بجاش undefined دریافت کنیم. تایپ اسکریپت هیچ اخطاری برای این مشکل به ما نمیده:

function logUser (id: number) {
  const user = users.find(u => u.id === id);

  console.log(user.name); // هیج اخطاری نمیده
}

ولی اگه تنظیم strictNullChecks رو فعال کنیم تایپ اسکریپت undefined و null رو نوع های مجزا در نظر میگیره و برای کد بالا خطا میگیره:

...
  console.log(user.name); // ⚠ خطا user ممکنه تعریف-نشده باشه
...

قدم بعدی

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

برای یادگیری بهتر حتما آموزش های تایپ اسکریپتی رسانیکا و سایر سایت ها رو هم ببینید. البته صرفا با مطالعه یا دیدن دوره های آموزشی نمیشه مهارت لازم رو کسب کرد و سعی کنید همراه با این مطالعات پروژه های کاملی هم بسازید.

برای تست کد تایپ اسکریپت توی مرورگر هم می تونید از TS Playground استفاده کنید.


کامنت ها