یکی از ویژگی های دیگری که به ما اجازه می دهد در رابطه با خصوصیات موجود در اشیاء منعطف تر باشیم index property نام دارد. فرض کنید فرمی در فایل HTML خود داشته باشیم که تعداد زیادی input در آن باشد. ما می خواهیم این input ها را چک کنیم و در صورت وجود خطا، خطای مناسبی را به کاربر نمایش دهیم. برای شروع یک interface جدید به نام Error Container را تعریف می کنم. هدف ما این است که بر اساس این interface جدید اشیائی بسازیم که درون خود یک id برای خطای کاربر داشته باشیم (که باید نام همان فیلد باشد) و سپس یک رشته که حاوی پیام خطا باشد. همچنین یک خصوصیت دیگر نیز داشته باشیم که مربوط به username باشد و پیام خطا را به کاربر نمایش دهد:
interface ErrorContainer { // { email: 'Not a valid email', username: 'Must start with a character!' } }
ساختاری را که توضیح دادم در قالب کامنت در کد بالا آورده ام. اگر از روش هایی که تا به حال یاد گرفته ایم برای کدنویسی این interface استفاده کنیم (نوشتن خصوصیت به صورت عادی به همراه تایپ در جلوی آن) حتما به دو مشکل برمی خوریم:
در چنین حالتی باید از index type ها استفاده کنیم. برای استفاده از آن ها به جای نوشتن نام خصوصیت email یا username باید از دو براکت استفاده کنیم:
interface ErrorContainer { // { email: 'Not a valid email', username: 'Must start with a character!' } [prop: string]: string; }
قسمت سمت چپ علامت دو نقطه، نام و نوع خصوصیت و قسمت سمت راست مقدار آن است. کد بالا به زبان ساده می گوید شیء ای که بر اساس این interface ساخته شود دارای خصوصیت هایی از نوع رشته ای است که مقادیری رشته ای می گیرند. شما می توانید به جای prop از هر چیز دیگری به سلیقه خودتان استفاده کنید و در واقع این نام اهمیتی ندارد، این نام به تایپ اسکریپت می گوید، نمی دانیم نام یا تعداد خصوصیت های من چه خواهد بود.
البته برخی اوقات قسمتی از کدهایتان را می دانید که می توانید آن ها را به کد بالا اضافه کنید البته تنها به شرطی که تایپ آن ها با تایپ index برابر باشد. به طور مثال:
interface ErrorContainer { // { email: 'Not a valid email', username: 'Must start with a character!' } id: string; [prop: string]: string; }
فرض من در کد بالا این است که اشیاء ساخته شده بر اساس این interface حتما خصوصیت id را دارند بنابراین می توانیم بدین شکل از آن ها استفاده کنیم اما اگر تایپ id را برابر number قرار بدهم با خطا روبرو می شویم چرا که index پایین آن از نوع رشته است و id دیگر نمی تواند برابر number باشد. حالا به نسخه بدون id برمی گردم و یک شیء بر اساس آن می سازم:
const errorBag: ErrorContainer = { email: 'Not a valid email!', username: 'Must start with a capital character!' };
همچنین اگر شیء بالا را خالی بگذاریم باز هم مشکلی نیست چرا که index type ها منعطف هستند و الزامی در وجود آن ها نیست. حتی اگر فقط email یا username را داشته باشیم مشکلی نیست.
قابلیت function overload به ما اجازه می دهد راه های مختلفی برای فراخوانی توابع با پارامترهای مختلف داشته باشیم. برای درک این موضوع باید به صورت عملی کار کنیم. اگر یادتان باشد در جلسات قبل چنین تابعی را نوشته بودیم:
type Combinable = string | number; function add(a: Combinable, b: Combinable) { if (typeof a === 'string' || typeof b === 'string') { return a.toString() + b.toString(); } return a + b; }
اگر موس خود را روی add ببرید، تایپ اسکریپت به شما می گوید که return type این تابع نیز از نوع combinable خواهد بود اما اگر به دقت به آن فکر کنید این موضوع درست نیست. Return type همیشه combinable (یا رشته یا عدد) نخواهد بود بلکه اگر به تابع عدد پاس بدهیم return type نیز عدد و اگر حداقل یک رشته پاس بدهیم return type نیز رشته خواهد بود. به نظر شما مشکل کجاست؟
به کد زیر توجه کنید:
const result = add(1, 5);
اگر تابع add را صدا زده و نتیجه را در result ذخیره کنیم و موس خود را روی آن ببریم، تایپ اسکریپت می گوید که result از نوع combinable است! اما ما می دانیم که result یک عدد است. اینکه تایپ اسکریپت نمی داند result یک عدد است بعدا قابلیت های type checking را از ما می گیرد. مثلا کد زیر را در نظر بگیرید:
const result = add('Max', ' Schwarz');
در اینجا result باز هم combinable است و تایپ اسکریپت نمی داند که با عدد طرف است یا با رشته بنابراین نمی توانیم از متدهای رشته روی result استفاده کنیم:
const result = add('Max', ' Schwarz'); result.split(' ');
با انجام این کار تایپ اسکریپت از من خطا می گیرد که split یک متد رشته ای است اما معلوم نیست result از چه نوعی باشد. یکی از راه حل های موجود استفاده از Type casting است که در جلسه قبل با آن آشنا شدیم:
const result = add('Max', ' Schwarz') as string;
حالا result از نوع String خواهد بود، نه combinable. روش دیگر استفاده از function overload است. برای انجام این کار کلمه function و نام تابع (add) را می آوریم و سپس تک تک حالت های ممکن را مشخص می کنیم:
function add(a: number, b: number): number; function add(a: string, b: string): string; function add(a: string, b: number): string; function add(a: number, b: string): string; function add(a: Combinable, b: Combinable) { if (typeof a === 'string' || typeof b === 'string') { return a.toString() + b.toString(); } return a + b; } const result = add('Max', ' Schwarz'); result.split(' ');
یعنی برای تابع add اگر a و b از نوع عدد باشند، return type نیز عدد خواهد بود، اگر a رشته و b رشته باشند، return type نیز رشته خواهد بود و الی آخر. البته شما الزامی به ذکر کردن تمام حالت های مختلف و ممکن ندارید بلکه می توانیم فقط دو حالت را مشخص کنیم:
function add(a: number, b: number): number; function add(a: string, b: string): string; function add(a: Combinable, b: Combinable) { if (typeof a === 'string' || typeof b === 'string') { return a.toString() + b.toString(); } return a + b; }
این مسئله به خود شما بستگی دارد. به هر حال از اینجا به بعد تایپ اسکریپت می فهمد که نوع دقیق return type شما چیست.
در این قسمت، به پرسشهای تخصصی شما دربارهی محتوای مقاله پاسخ داده نمیشود. سوالات خود را اینجا بپرسید.