Union Type و Literal Type در تایپ اسکریپت

27 اسفند 1398
Union Type و Literal Type در تایپ اسکریپت

تایپ Union Type

یکی دیگر از تایپ های بسیار جالب در تایپ اسکریپت union type ها (به معنی تایپ متحد یا مجموع) هستند. این نوع از داده ها معمولا ترکیب دو یا چند مورد از تایپ های قبلی هستند. بگذارید با مثال برایتان توضیح بدهم:

function combine (input1: number, input2: number) {
  const result = input1 + input2;
  return result;
}

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

const combinedAges = combine(30, 26);
console.log(combinedAges);

اگر این کار را انجام بدهیم هیچ مشکلی نخواهیم داشت و نتیجه ی 56 در console مرورگر ما نمایش داده می شود اما اگر بخواهیم دو رشته را با هم جمع کنیم، تایپ اسکریپت به ما خطا خواهد داد:

const combinedNames = combine('Max', 'Anna');
console.log(combinedNames);

چنین کدی غیر مجاز است چرا که بالاتر تایپ ورودی ها را number گذاشته ایم. اگر برگردیم و این تایپ را string بگذاریم کد بالا بدون خطا می شود اما کد قبل از آن که اعداد را جمع می کرد با خطا روبرو خواهد شد. اینجاست که union type ها وارد می شوند و به ما اجازه می دهند تایپی تعریف کنیم که جمع بین اعداد و رشته ها باشد. به طور مثال برای تعیین تایپ پارامترهای تابع combine می توان گفت:

function combine(input1: number | string, input2: number | string) {
  const result = input1 + input2;
  return result;
}

علامت بین number و string به pipe معروف است که یک خط عمودی است. در ضمن محدودیتی به استفاده از دو تایپ نیست و می توانید با ساختار بالا هر چند تایپ که مد نظرتان بود به تابع اضافه کنید. حالا به محض این که چنین کاری را انجام دهید به خطا برمی خورید و تایپ اسکریپت به شما می گوید که اپراتور + برای تایپ عدد و رشته به طور همزمان به کار نمی رود. همانطور که می دانید ظاهر این حرف اشتباه است و ما می توانیم از این اپراتور برای اعداد و رشته ها استفاده کنیم. تایپ اسکریپت فقط نگاه می کند تا ببیند تایپ شما چیست و زمانی که می فهمد union type است دیگر درون آن را بررسی نمی کند. در واقع تایپ اسکریپت به شما می گوید که باید برای چنین مواردی دقیق تر کد بنویسید بنابراین ما هم همین کار را می کنیم:

function combine(input1: number | string, input2: number | string) {
  let result;
  if (typeof input1 === 'number' && typeof input2 === 'number') {
    result = input1 + input2;
  } else {
    result = input1.toString() + input2.toString();
  }
  return result;
}

همانطور که می دانید اپراتور typeof نوع یا type یک متغیر را به ما می دهد. در اینجا با استفاده از این اپراتور گفته ایم که اگر تایپ پارامتر ورودی از نوع number بود آن ها را جمع بزن اما اگر اینطور نبود حتما رشته است بنابراین با استفاده از تابع toString مطمئن می شویم که ورودی ها به رشته تبدیل شده اند و سپس آن ها را جمع می زنیم.

حالا اگر کد زیر را داشته باشیم:

const combinedAges = combine(30, 26);
console.log(combinedAges);

const combinedNames = combine('Max', 'Anna');
console.log(combinedNames);

و به مرورگر برویم، هر دو کد بدون خطا اجرا خواهند شد:

نمایش خروجی های متفاوت دستور log
نمایش خروجی های مختلف دستور log

Literal Type چیست؟

Literal type ها به جای آنکه بگویند پارامتر یا متغیر من باید نوع داده ی خاصی را بگیرد، خود داده را مشخص می کنند. قبلا چنین چیزی را دیده بودیم:

const number = 5;

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

نمایش تایپ ثابت برای const ها
نمایش تایپ ثابت برای const ها

یعنی تایپ دیگر «عدد» نیست بلکه 5 است چرا که مقدار ثابت ها هیچ وقت تغییر نمی کند.

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

function combine(input1: number | string, input2: number | string, resultConversion: string) {

من می خواهم کاری کنم که هنگام صدا زدن این تابع بتوانیم چنین کاری انجام دهیم:

const combinedAges = combine(30, 26, 'as-number');

as-number یعنی «به عنوان عدد». به عبارت دیگر با استفاده از پارامتر سوم تعیین کنیم که دو پارامتر اول و دوم به صورت رشته یا به صورت عدد با هم جمع شوند! البته شما می توانید به جای as-number مقدار دلخواه خود را انتخاب کنید. من می خواهم سه حالت را بررسی کنم:

const combinedAges = combine(30, 26, 'as-number');
console.log(combinedAges);

const combinedStringAges = combine('30', '26', 'as-number');
console.log(combinedStringAges);

const combinedNames = combine('Max', 'Anna', 'as-text');
console.log(combinedNames);

یک بار اعداد را به صورت عدد، بار دیگر رشته های حاوی اعداد را به صورت اعداد و در نهایت رشته ها را به صورت رشته ای با هم جمع کنیم. در حال حاضر تابع ما آمادگی چنین کاری را ندارد چرا که پارامتر سوم را از نوع string تعیین کرده ایم که یعنی موقع صدا زدن تابع می توانیم هر مقدار رشته ای را در آن وارد کنیم. من می خواهم فقط دو مورد as-number و as-text قابل قبول باشند نه هر مقدار رشته ای.

یکی از راه های حل این مشکل این است که بگوییم:

function combine(input1: number | string, input2: number | string, resultConversion: string) {
  let result;
  if (typeof input1 === 'number' && typeof input2 === 'number') {
    result = input1 + input2;
  } else {
    result = input1.toString() + input2.toString();
  }
  if (resultConversion === 'as-number') {
    return +result;
  } else {
    return result.toString();
  }
}

یعنی اگر as-number به عنوان پارامتر سوم پاس داده شد، result را تبدیل به عدد کنیم. البته برای متغیر combinedStringAges مقدار 3026 را می گیریم بنابراین در این قسمت اشتباه رخ می دهد.

راه دیگر به شکل زیر است:

function combine(input1: number | string, input2: number | string, resultConversion: string) {
  let result;
  if (typeof input1 === 'number' && typeof input2 === 'number' || resultConversion === 'as-number') {
    result = +input1 + +input2;
  } else {
    result = input1.toString() + input2.toString();
  }
  return result;
}

یعنی مقدار resultConversion را نیز چک کنیم و اگر چنین بود حتما input ها را با علامت + تبدیل به عدد کنیم (حتی اگر رشته باشند). حالا مقادیر صحیح 56 و 56 و MaxAnna به ترتیب در کنسول مرورگر نمایش داده می شوند.

با اینکه مقادیر صحیح به ما داده می شود اما هنوز هم تابع ما تابع خوبی نیست چرا که تایپ پارامتر سوم رشته است بنابراین ممکن است هر چیزی را در آن تایپ کنیم. مثلا یادمان برود و as-text را به صورت as_text تایپ کنیم. بنابراین به جای حفظ کردن این موارد باید کار دیگری بکنیم چرا که ممکن است ده ها مورد به این شکل داشته باشیم.

اینجاست که literal type ها به همراه union type ها به کمک ما می آیند:

function combine(
  input1: number | string,
  input2: number | string,
  resultConversion: 'as-number' | 'as-text'
) {
  let result;
  if (typeof input1 === 'number' && typeof input2 === 'number' || resultConversion === 'as-number') {
    result = +input1 + +input2;
  } else {
    result = input1.toString() + input2.toString();
  }
  return result;
}

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

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

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

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