بهترین رویه های تایپ اسکریپت | راهنمای مقدماتی

تایپ اسکریپت یک اکستنشن جاوا اسکریپت است که یادگیری آسانی دارد. نوشتن برنامههای مختلف با استفاده از تایپ اسکریپت کار آسانی است. با این حال، در نظر گرفتن همه کاربردها و نوشتن کد تایپ اسکریپت مطمئن شاید کار دشواری باشد. در این مقاله با بهترین رویه های تایپ اسکریپت آشنا خواهیم شد. این رویهها شامل جایگزینی حلقههای for-in با جایگزینهای بهتر، استفاده از Promise و اجتناب از کاربرد متدهای شبیه eval است.
عدم تکرار روی آرایه با استفاده از حلقه for-in
با توجه به معرفی حلقههای for-of و متدهای دریافت کلیدهای شیء، اینک دیگر استفاده از حلقههای for-in چندان مفید نیست. ضمناً این نوع حلقه روی پروتوتایپهای شیء نیز میچرخد که شاید مطلوب ما نباشد. همچنین ترتیب تکرار نیز تضمین نشده نیست. از این رو نباید از حلقههای for-in در کد استفاده کنیم و به جای نوشتن کدی مانند زیر:
for (const x in [1, 2, 3]) { console.log(x); }
بهتر است از کدی مانند زیر استفاده کنیم:
for (const x of [1, 2, 3]) { console.log(x); }
حلقههای for-in روی اندیسها میچرخند، در حالی که حلقههای for-of روی مدخلها میچرخند.
از متدهای شبیه eval استفاده نکنید
در جاوا اسکریپت و به صورت طبیعی در تایپ اسکریپت، متدهایی وجود دارند که رشتهها را دریافت کرده و آنها را همانند کد اجرا میکنند. به این منظور متد eval ارائه شده است که یک کد را از داخل رشته ورودی اجرا میکند. سازنده Function نیز یک تابع را از یک رشته ورودی بازگشت میدهد. همچنین تابعهای setTimeout و setInterval هر دو میتوانند کدی را از درون رشته اجرا کنند.
این وضعیت امکان اجرای هیچ نوع بهینهسازی را فراهم نمیسازد، زیرا کد درون یک رشته قرار گرفته است. همچنین یک مشکل امنیتی عمده ایجاد میکند، زیرا هر کس میتواند داخل یک رشته ورودی کدی قرار داده و به صورت بالقوه آن را اجرا کند.
از این رو نباید از هیچ تابعی که امکان اجرای کد درون رشته را فراهم میسازد، استفاده کنیم. اگر از setTimeout یا setInterval استفاده کنیم، باید یک callback را به جای یک رشته ارسال کنیم. برای نمونه به جای نوشتن کدی مانند زیر:
setTimeout('alert(`foo`);', 100);
کدی مانند زیر بنویسیم:
setTimeout(() => { alert(`foo`); }, 100);
همچنین به جای نوشتن کدی مانند زیر:
const add = new Function('a', 'b', 'return a + b');
باید کدهایی به صورت زیر بنویسیم:
setInterval(() => { alert(`foo`); }, 10000);
در هر صورت تلاش کنید از بهکارگیری تابعهای شبه eval خودداری کنید.
عدم تعیین نوع صریح متغیرهای مقداردهی شده
ما برای دادههایی که به صورت صریح یک عدد، رشته یا مقدار بولی انتساب یافته، نیازی به تعیین نوع صریح نداریم. دلیل این امر آن است که این نوع از روی کد به صورت بدیهی مشخص است. از این رو به جای نوشتن کدی مانند زیر:
const a: number = 10;
میتوانیم کدی مانند زیر بنویسیم:
const a = 10;
این اصل در مورد هر نوع مقدماتی مانند رشته یا مقادیر بولی نیز صدق میکند. اگر نوع بازگشتی، بدیهی باشد، در این صورت میتوانیم از حاشیهنویسی نوع خودداری کنیم. بنابراین به جای نوشتن کدی مانند زیر:
const a: number = Number('1');
میتوانیم کدی مانند زیر بنویسیم:
const a = Number('1');
این وضعیت در خصوص regex-ها نیز صدق میکند. اگر یک لفظ regex داشته باشیم، در این صورت نیازی به حاشیهنویسی نخواهیم داشت. از این رو به جای نوشتن کدی مانند زیر:
const a = Number('1');
میتوانیم کدی مانند زیر بنویسیم:
const a = /foo/;
استفاده صحیح از new و constructor
توجه کنید که new و constructor باید در کد به روشهای معتبری مورد استفاده قرار گیرند. از این رو نباید از آنها برای ایجاد وهلههای جدید کلاس یا به عنوان تابعهای سازنده استفاده کنیم. برای نمونه به جای نوشتن کدی مانند زیر:
class C { new(): C; }
باید کدی مانند زیر بنویسیم:
class C { constructor(){ //... } } const c = new C();
Constructor-ها تنها باید در کلاسها باشند. ما میتوانیم new را به عنوان یک امضا در اینترفیس مورد استفاده قرار دهیم:
interface I { new (): C; }
از Promise-ها در محلهای نامناسب استفاده نکنید
ما نباید از Promise-ها در محلهایی که به این منظور طراحی نشدهاند، بهره بگیریم. برای نمونه، نباید از آنها در گزارههای if یا داخل حلقهها استفاده کنیم. از این رو به جای نوشتن کدهایی مانند زیر:
const promise = Promise.resolve('foo'); if (promise) { // Do something }
یا
const promise = Promise.resolve('foo'); while (promise) { // Do something }
یا
[1, 2, 3].forEach(async value => { await foo(value); });
یا
new Promise(async (resolve, reject) => { await doSomething(); resolve(); });
باید کدهایی مانند زیر بنویسیم:
const promise = Promise.resolve('foo'); if (await promise) { // Do something }
یا
const promise = Promise.resolve('foo'); while (await promise) { // Do something }
یا
for (const value of [1, 2, 3]) { await foo(value); }
همچنین باید await را در مکان مناسبی قرار دهیم تا مقدار resolve-شده را به صورت صحیحی به دست آوریم. کد زیر نیز استفاده نادرستی محسوب میشود:
new Promise(async (resolve, reject) => { await doSomething(); resolve(); });
دلیل نامناسب بودن کد فوق این است که داشتن یک promise درون یک promise دیگر موجب ایجاد افزونگی میشود. ما میتوانیم ()await doSomething را به خارج از callback مربوط به promise انتقال دهیم.
سخن پایانی
Promise-ها یا باید به روش مفیدی استفاده شوند و یا کلاً نباید از آنها استفاده کرد. تابعهای شبیه eval نباید مورد استفاده قرار گیرند، زیرا تولید ریسک میکنند. حلقههای for-in باید با جایگزینهای بهتری عوض شوند.