آشنایی با Function Type ها و callback ها!

27 اسفند 1398
آشنایی با Function Type ها و callback ها!

آشنایی با Function Type ها و callback ها در تایپ اسکریپت

تا اینجای کار با void و undefined نیز آشنا شدیم اما چه می شود اگر بتوانیم از توابع به عنوان یک تایپ خاص استفاده کنیم؟ بگذارید منظور خودم را شفاف تر بگویم. حتما از جلسه ی قبل دو تابع زیر را به یاد دارید:

function add(n1: number, n2: number): number {
  return n1 + n2;
}

function printResult(num: number): void {
  console.log('Result: ' + num);

حالا فرض کنید متغیری به شکل زیر داشته باشیم:

let combineValues;

در چنین حالتی، تایپ متغیر combineValues از نوع any است اما همانطور که گفتیم any کاربرد زیادی ندارد به همین خاطر می خواهم چنین کاری انجام بدهم:

combineValues = add;

یعنی تابع add را درون combineValues قرار بدهیم. همانطور که می دانید در جاوا اسکریپت می توانیم چنین کاری انجام بدهیم و حتی این متغیر را به صورت تابع اجرا کنیم:

let combineValues;

combineValues = add;
console.log(combineValues(8, 8));

اگر این کد را با دستور tsc app.ts کامپایل کرده و کد را درون مرورگر باز کنیم، عدد 16 را دریافت خواهیم کرد. مشکل اینجاست که در حال حاضر متغیر combineValues از نوع any است بنابراین هر مقداری را قبول می کند، حتی اگر به آن یک عدد بدهیم:

let combineValues;

combineValues = add;
combineValues = 5;
console.log(combineValues(8, 8));

کد بالا بدون خطا کامپایل می شود اما در مرورگر به خطا برخورد می کنیم چرا که یک اشتباه منطقی انجام داده ایم. با انتساب عدد 5 به combineValues، آن را تبدیل به یک متغیر عادی کرده ایم که عدد 5 را درون خود دارد و مشخصا چنین متغیری قابل اجرا شدن نیست و خطا دریافت می کنیم. برای حل این مشکل می توان تایپ متغیر را روی function تنظیم کرد:

let combineValues: Function;

combineValues = add;
combineValues = 5;
console.log(combineValues(8, 8));

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

let combineValues: Function;

combineValues = add;
combineValues = printResult;
console.log(combineValues(8, 8));

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

Result: 8

undefined

چرا که printResult فقط یک پارامتر می گیرد ولی ما آن را روی combineValues تنظیم کرده و سپس به combineValues دو پارامتر داده ایم! تایپ اسکریپت هم نمی تواند هیچ خطایی از کد ما بگیرد چرا که فقط تایپ function را برایش تعیین کرده ایم و printResult نیز یک تابع است.

راه حل ما function type ها هستند:

let combineValues: () => number;

همانطور که می بینید این syntax شباهت بسیار زیادی به arrow function ها دارد اما ربطی به آن ها ندارد. ما با قرار دادن علامت پرانتز و سپس علامت پیکان می گوییم که تایپ combineValues قرار است یک تابع باشد (البته تابعی که هیچ پارامتری قبول نمی کند) و سپس با اضافه کردن number، خروجی یا return آن را مشخص می کنیم که در اینجا number است.

تابع ما پارامتر می گیرد بنابراین باید این پارامترها را نیز به آن اضافه کنیم:

let combineValues: (a: number, b: number) => number;

combineValues = add;
combineValues = printResult;

حالا مشخص کرده ایم که combineValues تابعی می گیرد که دو پارامتر داشته باشد و هر دو عدد باشند و در نهایت یک عدد هم پس بدهد. همچنین نیازی نیست که نام این پارامترها دقیقا مشابه تابع اصلی باشند بلکه به جای آن هر چیزی (مثل a و b) را می توان قرار داد. با انجام این کار خط دوم (انتساب add) بدون مشکل خواهد بود اما برای خط سوم (انتساب printResult) با خطا روبرو می شویم چرا که تعریف ما بر printResult منطبق نیست (دو آرگومان در تعریف ما به جای یک آرگومان در printResult و همچنین return type از نوع void در printResult در برابر number در تعریف ما).

ما می توانیم همین منطق را برای توابع callback نیز پیاده کنیم (مطمئن هستم با callback ها آشنا هستید). به طور مثال تابع زیر را در نظر بگیرید:

function addAndHandle(n1: number, n2: number, cb) {
  const result = n1 + n2;
  cb(result);
}

پارامتر سوم این تابع cb یا همان callback است و قرار است تابعی باشد که درون تابع ما اجرا شده و مقدار result را دریافت می کند. حالا با همان منطق قبلی می توانیم نوع callback را نیز صریحا مشخص کنیم:

function addAndHandle(n1: number, n2: number, cb: (num: number) => void) {
  const result = n1 + n2;
  cb(result);
}

یعنی cb یک عدد را به عنوان آرگومان خود می خواهد و void را برمی گرداند. چرا void؟ به دلیل اینکه می خواهم cb دستور console.log را اجرا کند بنابراین هیچ دستور return ای نخواهد داشت. اگر شما بخواهید cb کار های بیشتری انجام داده و چیزی را return کند می توانید کد بالا را تغییر دهید. حالا به سادگی می توانیم آن را صدا بزنیم:

addAndHandle(10, 20, (result) => {
  console.log(result);
});

همانطور که می بینید برای پارامتر سوم یک تابع arrow function به آن داده ام (توجه کنید که این واقعا یک تابع است و type نیست – علامت دو نقطه ندارد) که مقدار دو پارامتر اول را console.log می کند. حالا اگر پس از کامپایل کد به مرورگر برویم به خروجی 30 روبرو می شویم که مقدار صحیح است.

البته نکته ی جالب اینجاست که اگر cb را با دستور return بنویسیم خطایی به ما داده نمی شود:

addAndHandle(10, 20, (result) => {
  console.log(result);
  return result;
});

ما بالاتر مشخص کردیم که cb باید خروجی void داشته باشد بنابراین چرا برای return به ما خطا نمی دهد؟ به دلیل اینکه void را در تابع addAndHandle مشخص کردیم و معنی آن این است که در تابع addAndHandle کاری به خروجی cb نداریم بنابراین void در اینجا یعنی خروجی تابع cb در تابع addAndHandle برایمان اهمیتی ندارد. به زبان ساده تر در چنین حالتی void نمی گوید تابع cb چیزی را return نمی کند بلکه می گوید اگر مقداری return شود برای ما اهمیتی ندارد (البته درون addAndHandle).

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

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

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