استفاده از ژنریک های تایپ اسکریپت — به زبان ساده

۷۸ بازدید
آخرین به‌روزرسانی: ۲۹ شهریور ۱۴۰۲
زمان مطالعه: ۳ دقیقه
استفاده از ژنریک های تایپ اسکریپت — به زبان ساده

همانند ساختار @@ در زبان روبی، نماد <> نیز به ژنریک های تایپ اسکریپت اختصاص دارد. اما بسیاری از برنامه‌نویسان از استفاده از این ژنریک ها واهمه دارند، در حالی که ژنریک ها اساساً مفهومی ساده و بسیار کارآمد دارند.

ژنریک ها تابع‌های سخت را نرم می‌سازند

همه ژنریک ها برای انعطاف‌پذیرتر ساختن تابع‌ها ساخته شده‌اند.

برای این که نشان دهیم چرا به آن‌ها نیاز داریم یک تابع آینه (mirror) می‌نویسیم. تنها کاری که این تابع انجام می‌دهد، بازگشت دادن چیزی است که در آن قرار می‌دهیم:

1const mirror1 = (thing: string) {
2  return thing;
3}
4mirror1('hello');
5> 'hello'
6mirror1(12) // breaks

این یک آغاز خوب است، اما عملکرد این آینه خوب نیست، چون تنها رشته به صورت string را بازتاب می‌دهد. ما می‌خواهیم این تابع همه چیزهایی که به آن ارسال می‌کنیم را بازتاب دهد. این همان جایی است که ژنریک ها به کار می‌آیند.

افزودن پارامترهای نوع

شما می‌توانید یک نوع را با قرار دادن آن درون <> پیش از () به صورت پارامتر تعریف کنید:

1const mirror2 = <MirrorType>(
2  thing: MirrorType
3): MirrorType => {
4  return thing;
5}
6mirror1<number>(12);
7mirror1<string>('hi');
8// non arrow version: 
9function mirror3<MirrorType>(
10  thing: MirrorType
11): MirrorType {
12  return thing;
13}

تنها کاری که در کد فوق انجام دادیم، این است که به تابع خود اعلام می‌کنیم چه نوع داده‌ای را می‌خواهیم استفاده کنیم و سپس می‌توانیم آن را در هر کجا مورد ارجاع قرار دهیم. بدین ترتیب زمانی که از <number> استفاده می‌کنیم، معنی آن این است که می‌خواهیم آرگومان از نوع number باشد و یک نوع number بازگشت دهد. در دنیای واقعی، کاربردها معمولاً به این سادگی نیستند.

اینترفیس‌های ژنریک

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

1const copyMachine = <CopyType>(
2  itemToCopy: CopyType,
3  numOfCopies: number,
4) => {
5  return Array(numOfCopies)
6    .fill(itemToCopy);
7}
8copyMachine<string>('hi', 3);
9> ['hi', 'hi', 'hi']
10// more array tricks in this link

همچنین می‌توانید از انواع مختلف استفاده کنید:

1interface MagicTypes<Type1, Type2> {
2  firstItem: Type1;
3  secondItem: Type2;
4}
5const magicMirror = <Type1, Type2>(
6  arg1: Type1,
7  arg2: Type2,
8): MagicTypes<Type1, Type2> => ({
9  firstItem: arg1,
10  secondItem: arg2,
11});

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

آیا نیازی به ارسال نوع وجود دارد؟

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

1const mirror2 = <MirrorType>(
2  thing: MirrorType
3): MirrorType => {
4  return thing;
5}
6// still works
7mirror2('hi');

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

هیچ نوعی برای MirrorType ارائه نشده است، اما مقدار thing به صورت string است. این بدان معنی است که MirrorType نیز باید یک string باشد.

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

با و بدون اینترفیس

در مثال زیر شیوه بررسی شیء آرگومان تابع از سوی تایپ اسکریپت را می‌بینیم:

1mirror({
2  name: '',
3  mood: 'happy',
4  isHungry: 0,
5});
6// default TS types:
7{
8  name: string;
9  mood: string;
10  isHungry: number;
11}

این کد مشکلی ندارد، اما فرض کنید اینترفیس واقعی باشد:

1type NumberBoolean = 0 | 1;
2interface PersonType {
3  name: string;
4  mood: 'happy' | 'sad';
5  isHungry: NumberBoolean;  
6};

این کد بسیار خاص‌تر است و مزیت‌های دیگری به ما می‌دهد. برای استفاده از این اینترفیس آن را ارسال می‌کنیم:

1mirror<PersonType>({
2  name: '',
3  mood: 'happy',
4  isHungry: 0,
5});

اینک تایپ اسکریپت می‌داند که isHungry هرگز نمی‌تواند عددی مانند 34 باشد و mood تنها دو گزینه دارد.

هشدار در مورد JSX

پیش از آن که اقدام به تعریف ژنریک های خود بکنید، باید در مورد ایجاد تابع‌های arrow ژنریک در فایل‌های tsx. مراقب باشید. با این که کامپایلر تایپ اسکریپت می‌تواند استفاده از تابع‌های ژنریک مانند <useState<Interface را در React مدیریت کند، اما زمانی که آن‌ها را تعریف می‌کنیم، مشکلاتی بروز می‌یابد. تایپ اسکریپت پارامتر نوع را ناگهان به عنوان یک تگ jsx در HTML تلقی می‌کند. دو گزینه برای رفع این مشکل وجود دارند:

  • عدم استفاده از تابع‌های arrow
  • بسط دادن {}

تابع‌های معمولی

اگر اتصال this برایتان مهم نیست، می‌توانید از تابع معمولی استفاده کنید:

1const mirrorTSX = function<ThingType>(
2    thing: ThingType
3) {
4  return thing;
5}
6// works! 
7mirrorTSX(12);

بسط دادن {}

استفاده از کلیدواژه extends به کامپایلر تایپ اسکریپت اعلام می‌کند که HTML را نمی‌خواند:

1const mirrorTSX = <ThingType extends {}>(
2    thing: ThingType
3) => {
4  return thing;
5}
6mirrorTSX('hi');

تعریف کردن ژنریک ها درون فایل‌های tsx. چندان رایج نیست، اما زمانی که آن خطا را دریافت می‌کنید و دلیل آن را نمی‌دانید بسیار آزاردهنده خواهد بود.

سخن پایانی

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

اگر این مطلب برای شما مفید بوده است، آموزش‌های زیر نیز به شما پیشنهاد می‌شوند:

==

بر اساس رای ۲ نفر
آیا این مطلب برای شما مفید بود؟
اگر بازخوردی درباره این مطلب دارید یا پرسشی دارید که بدون پاسخ مانده است، آن را از طریق بخش نظرات مطرح کنید.
منابع:
itnext
نظر شما چیست؟

نشانی ایمیل شما منتشر نخواهد شد. بخش‌های موردنیاز علامت‌گذاری شده‌اند *