آشنایی با Generics: به فصل جدید خوش آمدید!

Introduction to Generics

12 مرداد 1399
آشنایی با Generics: به فصل جدید خوش آمدید!

Generics از ویژگی های مخصوص تایپ اسکریپت هستند که در جاوا اسکریپت وجود ندارند. برخی از زبان های برنامه نویسی مفهوم Generics را در خودشان پیاده سازی کرده اند (مثل زبان #C) اما متاسفانه جاوا اسکریپت در این زمینه عقب مانده است. برای شروع کار از پروژه ساده خود شروع می کنیم. برای شروع کار یک پروژه خالی و از قبل تنظیم شده را برای شما قرار می دهم که باید آن را دانلود کرده و در سیستم خود باز کنید (دانلود پروژه). درون این پروژه ترمینال را باز کرده و npm install را اجرا کنید. پس از نصب پکیج های وابسته می توانید دستور npm start را اجرا کنید تا بتوانیم کار را به صورت خودکار در مرورگر مشاهده کنیم. همچنین در یک ترمینال جدید دستور tsc -w را اجرا کنید تا با ذخیره فایل ها، فرآیند کامپایل شدن به صورت خودکار انجام بگیرد. از نظر من بهترین راه آشنایی با Generic ها استفاده از یکی از تایپ های زبان تایپ اسکریپت است که قبلا با آن کار کرده ایم: آرایه ها!

فرض کنید آرایه ای از اسامی داشته باشیم:

const names = ['Max', 'Manuel'];

تایپ متغیر names برابر با آرایه رشته ای است که به صورت زیر نمایش داده می شود:

const names: string[]

اگر مقادیر درون این آرایه را حذف کنم چطور؟

const names = [];

تایپ متغیر names در این حالت برابر با یک آرایه از نوع any است:

const names: any[]

همه ما می دانیم که تایپی به نام Array در تایپ اسکریپت وجود دارد بنابراین اگر بخواهیم به جای آرایه ای از نوع Any بگوییم names فقط یک آرایه است چه می شود؟ اگر تایپ آن را فقط روی Array تنظیم کنیم چه اتفاقی می افتد؟

const names: Array = [];

کد بالا به ما خطای زیر را می دهد:

Generic type 'Array<T>' requires 1 type argument(s).

به زبان ساده تر Generic به معنای «عمومی» یا «کلی» است و تایپ های Generic یعنی تایپ هایی که عمومی هستند و به طور خاص نوع داده ای را مشخص نمی کنند. در تعریف های فنی تر می گویند generic type ها تایپ های منعطفی هستند که به تایپ دیگری متصل می شوند. به طور مثال به متغیر names نگاه کنید که یک آرایه است. آرایه ها لیستی از داده ها هستند که خودش می شود یک تایپ خاص، تایپی که برای یک مجموعه داده مشخص می شود. از طرفی هر آرایه داده های مختلفی را درون خود دارد که ممکن است رشته، عدد و ... باشند. این داده ها یک تایپ دیگر را می سازند که مخصوص محتوای درون آرایه است. به همین دلیل است که تایپ Array در کد بالا به ما خطا می دهد. Array می داند که باید یک تایپ دیگر نیز بگیرد و حتی اگر شما نمی دانید تایپ داده های درون آن چه چیزی است باید آن را به Array اعلام کنید (با تعیین تایپ []any).

مسئله ای که تا به حال با آن روبرو نشدیم، روشی جدید برای مشخص کردن تایپ دوم است. همه می دانیم که می توانیم به شکل زیر تایپ یک آرایه رشته ای را مشخص کنیم:

string[]

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

const names: Array<string> = []; // string[]

یعنی می توانیم پس از مشخص کردن generic type با استفاده از علامت های <> (معروف به angle brackets) تایپ دقیق تر را مشخص می کنیم.  حتی می توانید از union type ها درون این قسمت استفاده کنید:

const names: Array<string | number> = []; // string[]

یا به طور مثال می گوییم any که یعنی هر نوع داده ای:

const names: Array<any> = []; // string[]

حالا متوجه تعریف فنی تر می شوید: generic type ها تایپ هایی هستند که به یک تایپ دیگر متصل می شوند!

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

names[0].split(' ');

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

یکی دیگر از generic type ها، promise ها هستند که در نسخه های جدیدتر جاوا اسکریپت وجود دارند. ما در دوره جاوا اسکریپت نامتقارن و پروژه های مدرن جاوا اسکریپت چندین بار در رابطه با promise ها صحبت کرده ایم. در صورتی که چیزی از promise ها نمی دانید به صفحه توسعه دهندگان موزیلا در این زمینه مراجعه کنید: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise

برای ساختن یک promise جدید در جاوا اسکریپت از کلیدواژه new استفاده می کردیم. خود promise یک پارامتر می گیرد که یک تابع است و خود دارای دو پارامتر به نام های resolve و reject می باشد که به صورت خودکار توسط خود جاوا اسکریپت به ما پاس داده می شوند:

const promise = new Promise((resolve, reject) => { });

من یک promise را برای نمونه می نویسم:

const promise = new Promise((resolve, reject) => {
  setTimeout(() => {
    resolve('This is done!');
  }, 2000);
});

ما از یک تایمر استفاده کرده ایم که پس از 2 ثانیه promise را resolve می کند و می گوید this is done (یعنی کار انجام شده است). در واقع promise بالا یک شیء می سازد که درون ثابت promise ذخیره می شود. اگر موس خود را روی promise ببرید تایپ آن را به شکل زیر نمایش می دهد:

const promise: Promise<unknown>

در اینجا تایپ اسکریپت نمی تواند تشخیص دهد که resolve ما یک رشته ساده است بنابراین می توانیم خودمان آن را با استفاده از Generic type ها مشخص کنیم:

const promise: Promise<string> = new Promise((resolve, reject) => {
  setTimeout(() => {
    resolve('This is done!');
  }, 2000);
});

Promise ها هم در نهایت باید داده ای را برگردانند بنابراین ابتدا تایپ خود Promise را داریم و سپس تایپی که promise برمی گرداند را خواهیم داشت بنابراین به همان تعریف فنی Generic ها می رسیم که یک تایپ به تایپ دیگری متصل می شود. اگر ما تایپ این promise را مشخص نکنیم، استفاده از then برایمان سخت تر می شود. مثلا:

const promise: Promise<any> = new Promise((resolve, reject) => {
  setTimeout(() => {
    resolve('This is done!');
  }, 2000);
});

promise.then(data => {
  data.split(' ');
})

اینجا از any استفاده کرده ام. اگر بخواهم متد Split را روی رشته برگردانده شده از promise صدا بزنم مشکلی نیست ولی تایپ اسکریپت نمی داند داده برگردانده شده (Data) از چه نوعی است بنابراین auto completion و پیشنهاد های تایپ اسکریپت را از دست می دهیم. حتی اگر مقدار resolve را به یک عدد تغییر بدهم باز هم می توانم از Split استفاده کنم:

const promise: Promise<any> = new Promise((resolve, reject) => {
  setTimeout(() => {
    resolve(10);
  }, 2000);
});

promise.then(data => {
  data.split(' ');
})

در صورتی که نباید چنین اتفاقی بیفتد. ما اجازه نداریم از متدهای رشته ای مثل split روی اعداد استفاده کنیم اما تایپ اسکریپت به ما خطایی نمی دهد چرا که اصلا تشخیص نمی دهد resolve از چه نوعی است. تا اینجا فقط در رابطه با Generic type هایی صحبت کردیم که در خود تایپ اسکریپت وجود دارند اما در جلسه بعد generic type هایی را به سلیقه خودمان خواهیم ساخت!

تمام فصل‌های سری ترتیبی که روکسو برای مطالعه‌ی دروس سری آموزش جامع تایپ اسکریپت توصیه می‌کند:
نویسنده شوید

دیدگاه‌های شما

در این قسمت، به پرسش‌های تخصصی شما درباره‌ی محتوای مقاله پاسخ داده نمی‌شود. سوالات خود را اینجا بپرسید.

مقالات مرتبط
ما را دنبال کنید
اینستاگرام روکسو تلگرام روکسو ایمیل و خبرنامه روکسو