# مبانی TypeScript؛ متدها

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

- Published: 2016-03-26
- Language: fa
- Tags: DNTips
- Canonical: https://sirwan.info/blog/fa/dntips-2351

---

> این نوشته نخستین بار در [دات‌نت تیپس](https://www.dntips.ir/post/2351) منتشر شده است.

<div class="postBody">توابع جزو ساختارهای پایه‌ایی زبان جاوا اسکریپت هستند و از آنها جهت شبیه‌سازی کلاس‌ها، کپسوله‌سازی اطلاعات و همچنین ایجاد ماژول‌ها استفاده می‌شود. هر چند در زبان TypeScript به راحتی می‌توانیم از کلاس‌ها، فضاهای نام و ماژول‌ها استفاده کنیم، اما هنوز هم توابع، نقشی کلیدی را در انجام کارها ایفا می‌کنند. نکته‌ی قابل توجه این است که زبان TypeScript یکسری قابلیت‌های اضافه‌تری را به توابع استاندارد جاوا اسکریپت اضافه کرده است که در ادامه آنها را بررسی خواهیم کرد. <div> <b> <br/> </b> </div> <div> <b>نحوه‌ی ایجاد توابع</b> </div> <div>همانند جاوا اسکریپت، در زبان TypeScript نیز می‌توانیم توابع را هم به صورت named function و  هم به صورت anonymous function ایجاد کنیم. در کدهای زیر نحوه‌ی تعریف هر دو نوع نشان داده شده است: </div> <div> <div align="left" dir="ltr" style="direction: ltr;">
<pre language="TypeScript" name="code">// Named function&#10;function add(x, y) {&#10;    return x + y;&#10;}&#10;&#10;// Anonymous function&#10;let myAdd = function(x, y) { return x+y; };</pre>
 </div>
در زبان TypeScript نیز می‌توانیم به متغیرهای تعریف شده‌ی در خارج بدنه‌ی تابع، دسترسی داشته باشیم. در این‌حالت خواهیم گفت که متغیرها توسط تابع capture شده‌اند:</div> <div> <div align="left" dir="ltr" style="direction: ltr;">
<pre language="TypeScript" name="code">let z = 100;&#10;&#10;function addToZ(x, y) {&#10;    return x + y + z;&#10;}</pre>
 </div> <b> <div> <b> <br/> </b> </div>
تعیین نوع (Type) برای توابع</b> </div> <div>همانطور که قبلاً عنوان شد، یکی از مزایای زبان TypeScript، امکان معرفی نوع‌ها است. یعنی با کمک تعیین نوع می‌توانیم کدهای نهایی نوشته شده را امن‌تر کنیم و همچنین در زمان استفاده، Intellisense با وجود نوع‌ها، پیشنهادهای بهتر و دقیق‌تری را ارائه می‌دهد. جهت درک بهتر موضوع اجازه دهید برای توابعی که در مثال قبلی مطرح شدند، یکسری نوع را مشخص کنیم:</div> <div> <div align="left" dir="ltr" style="direction: ltr;"> <div align="left" dir="ltr" style="direction:ltr;text-align:left;">
<pre language="TypeScript" name="code">function add(x: number, y: number): number {&#10;    return x + y;&#10;}&#10;&#10;let myAdd = function(x: number, y: number): number { return x+y; };</pre>
 </div> </div>
همانطور که مشاهده می‌کنید، توانسته‌ایم هم برای پارامترها و هم برای خروجی تابع، نوع‌هایی را مشخص کنیم. مزیت آن این است که پیش از اینکه کدهای شما در زمان اجرا به خطا بر بخورند، در زمان کامپایل، مشکلات موجود توسط کامپایلر، گوشزد می‌شوند. به عنوان مثال اگر در زمان توسعه، برای یکی از پارامترهای تابع add، مقداری رشته‌ایی را ارسال کنیم، کامپایلر به شما هشدار لازم را خواهد داد:<br/> <br/> </div> <div> <p style=" margin-left: auto; margin-right: auto;"> <img src="/img/dntips/6827de6d6972c61a0314.jpg" style="display: block; margin-left: auto; margin-right: auto; cursor: default; height: 518px; width: 834.226px;"/> </p> </div> <div> <b> <br/> </b> </div> <div> <b>  Function Types<br/> </b> </div> <div>در TypeScript علاوه بر امکان تعیین نوع، برای پارامتر و همچنین نوع بازگشتی تابع، می‌توانیم خود تابع را نیز به عنوان یک نوع تعریف کنیم. function types با تعیین نوع برای پارمتر دریافتی و همچنین تعیین نوع بازگشتی تابع تعریف می‌شوند. به عنوان مثال تابع زیر، یک پارامتر از نوع number را دریافت کرده و در نهایت یک رشته را در خروجی بر می‌گرداند:</div> <div> <div align="left" dir="ltr" style="direction: ltr;">
<pre language="TypeScript" name="code">function PublicationMessage(year: number): string {&#10;    return 'Date published: ' + year;&#10;}</pre>
 </div>
اکنون می‌توانیم یک متغیر ایجاد کنیم که ارجاعی را به تابع فوق داشته باشد:</div> <div> <div align="left" dir="ltr" style="direction: ltr;">
<pre language="TypeScript" name="code">let publishFunc: (someYear: number) =&gt; string;</pre>
 </div>
با استفاده از سینتکس فوق، توانسته‌ایم یک function type را تعریف کنیم. در کد فوق از کلمه‌ی کلیدی let و همچنین علامت دو نقطه بعد از نام متغیر استفاده کرده‌ایم. سپس پارامترها و همچنین انواع آنها را درون پرانتز تعیین کرده‌ایم و در نهایت بعد از علامت &lt;=، نوع بازگشتی تابع را تعیین کرده‌ایم. در واقع توسط کد فوق، یک امضاء را برای توابعی که قرار است به این متغیر انتساب داده شوند، تعریف کرده‌ایم. اکنون که متغیر را تعریف کردیم و همچنین یک نوع را برای آن مشخص کردیم، می‌توانیم تابعی را که دارای این امضاء است، به آن انتساب دهیم:</div> <div> <div align="left" dir="ltr" style="direction: ltr;">
<pre language="TypeScript" name="code">publishFunc = PublicationMessage;&#10;&#10;let message: string = publishFunc(2016);</pre>
 </div>
در واقع اکنون به متغیر publishFunc، تنها تابعی را می‌توانیم انتساب دهیم که یک پارامتر از جنس number را از ورودی دریافت کرده و همچنین یک رشته را به عنوان خروجی برگرداند. در نتیجه اگر تابعی غیر از امضاء تعیین شده را به متغیر publishFunc انتساب دهیم، کامپایلر TypeScript به ما هشدار خواهد داد:<br/> <br/> </div> <div> <p style="margin-left: auto; margin-right: auto;"> <img src="/img/dntips/e7fde4c08f1342b168bb.jpg" style="display: block; margin-left: auto; margin-right: auto; cursor: default; width: 838.101px; height: 607px;"/> </p> <p style="margin-left: auto; margin-right: auto;">همچنین می‌توان function type را به صورت inline نیز تعریف کرد:</p> <div align="left" dir="ltr" style="direction: ltr;">
<pre language="TypeScript" name="code">let myAdd: (baseValue:number, increment:number) =&gt; number =&#10;    function(x, y) { return x + y; };</pre>
 </div> <p style="margin-left: auto; margin-right: auto;"> <b> <br/> </b> </p> <p style="margin-left: auto; margin-right: auto;"> <b>Optional and Default Parameters  </b> <br/> </p> <p style="margin-left: auto; margin-right: auto;">در جاوا اسکریپت تمامی پارامترهای یک تابع اختیاری هستند. اما TypeScript کمی متفاوت است. یعنی در حالت پیش‌فرض، ذکر تمامی پارامترها ضروری است؛ مگر اینکه پارامترهای موردنیاز را به صورت اختیاری تعیین کنید. به طور مثال در تابع زیر دو پارامتر را تعریف کرده‌ایم:</p> <div align="left" dir="ltr" style="direction: ltr;">
<pre language="TypeScript" name="code">function CreateCustomer(name: string, age?: number) {}</pre>
 </div> <p style="margin-left: auto; margin-right: auto;">همانطور که مشاهده می‌کنید با افزودن علامت سوال بعد از نام پارامتر، توانسته‌ایم آن را به صورت اختیاری تعریف کنیم. نکته‌ایی که در اینجا وجود دارد این است که تمامی پارامترهای optional، حتماً باید بعد از پارامترهای required تعریف شوند.</p> <p style="margin-left: auto; margin-right: auto;">برای تعیین مقدار پیش‌فرض برای هر پارامتر نیز می‌توانیم به این‌صورت عمل کنیم:</p> <div align="left" dir="ltr" style="direction: ltr;">
<pre language="TypeScript" name="code">function GetBookByTitle(title: string = 'C# 6.0 in a Nutshell') {}</pre>
 </div> <p style="margin-left: auto; margin-right: auto;">default parameters در صورتیکه بعد از required parameters آورده شوند، به عنوان optional در نظر گرفته می‌شوند. یعنی در این‌حالت لزومی به گذاشتن علامت سوال، بعد از نام پارامتر نیست. نکته‌ی قابل توجه‌ایی که در استفاده از default parameters وجود دارد این است که علاوه بر رشته‌ها می‌توان عبارات (expressions) را نیز به آنها اختصاص داد:</p> <div align="left" dir="ltr" style="direction: ltr;">
<pre language="TypeScript" name="code">function GetBookByTitle(title: string = GetMostPopularBooks()) {}</pre>
 </div> <p style="margin-left: auto; margin-right: auto;"> <b> <br/> </b> </p> <p style="margin-left: auto; margin-right: auto;"> <b>Rest Parameters</b> </p> <p style="margin-left: auto; margin-right: auto;"> <a href="https://www.dntips.ir/post/2294">rest parameters</a> به شما این امکان را می‌دهند تا به تعداد نامحدودی پارامتر به یک تابع ارسال کنید: </p> <div align="left" dir="ltr" style="direction: ltr;">
<pre language="TypeScript" name="code">function GetBooksReadForCust(name: string, ...bookIDs: number[]) {}</pre>
 </div> <p style="margin-left: auto; margin-right: auto;">تابع فوق دو پارامتر را از ورودی دریافت می‌کند. پارامتر دوم این تابع به صورت rest تعریف شده است. یعنی برای پارامتر دوم می‌توانیم هر تعداد پارامتری را به این تابع ارسال کنیم. همچنین برای نوع این پارامتر، یک آرایه از نوع number را تعیین کرده‌ایم. یعنی پارامترهای دریافتی، درون یک آرایه از نوع number ذخیره خواهند شد. در ES 5 برای داشتن این چنین قابلیتی از شیء  <a href="https://developer.mozilla.org/en/docs/Web/JavaScript/Reference/Functions/arguments">arguments</a> استفاده می‌کردیم. یعنی تابع فوق را می‌بایستی اینگونه می‌نوشتیم: </p> <div align="left" dir="ltr" style="direction: ltr;">
<pre language="JScript" name="code">function GetBooksReadForCust(name) {&#10;    var bookIDs = [];&#10;    for (var _i = 1; _i &lt; arguments.length; _i++) {&#10;        bookIDs[_i - 1] = arguments[_i];&#10;    }&#10;}</pre>
 </div> <p style="margin-left: auto; margin-right: auto;"> <b> <br/> </b> </p> <p style="margin-left: auto; margin-right: auto;"> <b>استفاده از this</b> </p> <p style="margin-left: auto; margin-right: auto;">درک this در جاوا اسکریپت، در ابتدا باعث مقداری سردرگمی می‌شود. یعنی مقدار آن در زمان فراخوانی تابع، ست خواهد شد. یعنی در هر بلاک از کد، وضعیت‌های متفاوتی را ارائه می‌دهد. به عنوان مثال درون callback مربوط به تابع setInterval در تابع زیر می‌خواهیم به مقدار متغیر publishDate دسترسی داشته باشم:</p> <div align="left" dir="ltr" style="direction: ltr;">
<pre language="JScript" name="code">function Book() {&#10;    let self = this;&#10;    self.publishDate = 2016;&#10;    setInterval(function() {&#10;        console.log(self.publishDate);&#10;    }, 1000);&#10;}</pre>
 </div> <p style="margin-left: auto; margin-right: auto;">همانطور که مشاهده می‌کنید برای دسترسی به این پراپرتی، مقدار this را درون یک متغیر با نام self، در ابتدا تعریف کرده‌ایم. زیرا استفاده‌ی مستقیم از this.publishDate درون callback به چیز دیگری اشاره می‌کند. این روش در ES 5 خیلی رایج است. اما با استفاده از <a href="https://www.dntips.ir/post/2306">Arrow Functions</a>  به راحتی می‌توانیم به this در هر جایی دسترسی داشته باشیم. بنابراین کد فوق را می‌توانیم به این صورت بازنویسی کنیم:
</p> <div align="left" dir="ltr" style="direction: ltr;">
<pre language="TypeScript" name="code">function Book() {&#10;    this.publishDate = 2016;&#10;    setInterval(() =&gt; {&#10;        console.log(this.publishDate);&#10;    }, 1000);&#10;}</pre>
 </div> <p style="margin-left: auto; margin-right: auto;">در واقع <a href="https://www.dntips.ir/post/2306">Arrow Function</a>  در پشت صحنه کار capture کردن this را برایمان انجام خواهد داد.</p> <p style="margin-left: auto; margin-right: auto;"> <br/> </p> <p style="margin-left: auto; margin-right: auto;"> <b>Function overloads</b> </p> <p style="margin-left: auto; margin-right: auto;">قابلیت function overloading در بیشتر typed languageها در دسترس می‌باشد. همانطور که می‌دانید این قابلیت جهت تعریف امضاءهای مختلف برای یک تابع استفاده می‌شود. یعنی ایجاد توابعی با یک نام، اما با انواع متفاوت. از آنجائیکه TypeScript به جاوا اسکریپت کامپایل می‌شود، در نتیجه جاوا اسکریپت فاقد نوع (type) است. پس در زمان کامپایل نوع‌ها برداشته خواهند شد. بنابراین داشتن توابعی همنام باعث بروز مشکلاتی خواهد شد. برای داشتن نسخه‌های مختلفی از یک تابع می‌توانیم تعاریف موردنیازمان را ارائه داده، اما تنها یک پیاده‌سازی داشته باشیم. برای مثال می‌خواهیم یک overload دیگر برای تابع زیر داشته باشیم:</p> <div align="left" dir="ltr" style="direction: ltr;">
<pre language="TypeScript" name="code">function GetTitles(author: string) : string[];</pre>
 </div> <p style="margin-left: auto; margin-right: auto;">تابع فوق یک رشته را از ورودی دریافت کرده و در نهایت یک آرایه از رشته‌ها را بر می‌گرداند. برای overload دیگر این تابع می‌خواهیم به جای دریافت رشته، یک boolean از ورودی دریافت کنیم:</p> <div align="left" dir="ltr" style="direction: ltr;">
<pre language="TypeScript" name="code">function GetTitles(available: boolean) : string[];</pre>
 </div> <p style="margin-left: auto; margin-right: auto;">همانطور که مشاهده می‌کنید، هیچکدام از overloadهای فوق پیاده‌سازی‌ایی ندارند. در واقع تا اینجا به TypeScript گفته‌ایم که نیاز به دو نسخه از تابع GetTitles خواهیم داشت. اکنون می‌توانیم یک پیاده‌سازی کلی برای دو overload فوق داشته باشیم:</p> <div align="left" dir="ltr" style="direction: ltr;">
<pre language="TypeScript" name="code">function GetTitles(bookProperty: any) : string[] {&#10;    if(typeof bookProperty == 'string') {&#10;        // some code&#10;    } else if (typeof bookProperty == 'boolean') {&#10;        // some code&#10;    }&#10;    &#10;    return result;&#10;}</pre>
 </div> <p style="margin-left: auto; margin-right: auto;">همانطور که عنوان شد، تنها پیاده‌سازی فوق را برای تمامی overloadها خواهیم داشت. در نتیجه اینبار نوع پارامتر ورودی را any تعریف کرده‌ایم. سپس درون بدنه‌ی تابع، نوع پراپرتی را توسط typeof تشخیص داده‌ایم. بنابراین برای فراخوانی هر یک از overloadها، می‌توانیم کدهای خاصی را اجرا کنیم.</p> </div></div>
