انواع کاربردی در تایپ اسکریپت — به زبان ساده
در این راهنما با انواع کاربردی در تایپ اسکریپت آشنا میشویم. منظور از «انواع کاربردی» (Utility Types)، انواع دادههای انعطافپذیری هستند که میتوان در موقعیتهای مختلف در این زبان مورد استفاده قرار داد.
تصور کنید مشغول کار روی پروژهای هستید که باید دادههای با حجم بالایی را در قالب JSON بنویسید و میخواهید سادهترین راهحل را به این منظور بیابید. بسیاری از رکوردها موارد مشترکی با هم دارند و برخی فیلدها هستند که نمیخواهیم تکرار شوند، زیرا رکوردهای زیادی وجود دارند که از مقدار یکسانی برخوردارند. همچنین میخواهیم فیلدهای خاصی مانند ID را محاسبه کنیم.
در این مورد یک روش هوشمندانه این است که از راهحلی مانند زیر استفاده کنیم:
1interface Unit {
2 id: number;
3 type: string;
4 name: string;
5 cost: number;
6 experience: number;
7}
8
9const INFANTRY_DATA: any[] = [{
10 name: 'Pikeman',
11 cost: 8
12},{
13 name: 'Rifleman',
14 cost: 10
15},{
16 name: 'Grenadier',
17 cost: 12
18}].map(unit => ({
19 ...unit,
20 type: 'Infantry'
21}));
22
23const CAVALRY_DATA: any[] = [{
24 name: 'Light Cavalry',
25 cost: 16
26},{
27 name: 'Heavy Cavalry',
28 cost: 20
29}].map(unit => ({
30 ...unit,
31 type: 'Cavalry'
32}));
33
34export const UNIT_DATA: Unit[] = [...INFANTRY_DATA, ...CAVALRY_DATA].map((unit, i) => ({
35 ...unit,
36 id: i,
37 experience: 0
38}));
ایده کار این است که آرایهای از رکوردهای ناکامل بسازیم و سپس هر آیتم آرایه را برای قرار دادن فیلدهای بیشتر نگاشت (map) کنیم.
این کار به منظور کاستن از حجم دادههای تکراری کاملاً مناسب است، اما در نهایت «امنیت نوع» (Type Safety) از دست میرود، زیرا برای اعلان آرایههای داده ناقص خودمان باید از نوع any استفاده کنیم.
امکان optional کردن فیلدهای مفقود در اینترفیس با درج علامت سؤال پس از نام متغیر وجود دارد، اما از آنجا که فیلدها در خروجی نهایی الزامی هستند، راهحل درستی محسوب نمیشود.
همچنین میتوانیم یک نوع جدید اعلان کنیم که دادههای ناقص را توصیف میکند، اما این راهحل نیز به نظر مناسب نمیرسد، چون ممکن است فیلدهای بیشتری از آن چه در مثال فوق دیدیم وجود داشته باشند. به علاوه نامگذاری موارد مختلف دشوار است، زیرا نمیخواهیم برای یک نوع ناقص که صرفاً برای کاهش موارد تکراری ساختهایم، نام نامناسبی داشته باشیم. بنابراین از روش زیر استفاده میکنیم:
const INFANTRY_DATA: any[]
به جهت شیوه کار نوع any، کامپایلر هیچ نوعی که مفقود باشد را بررسی نمیکند، همچنین انتساب نوع نادرست را بررسی نخواهد کرد. این وضعیتی مشکلزا است. برای نمونه اگر name در یک رکورد منفرد اشتباه نوشته شده باشد چطور میشود؟ ممکن است صدها رکورد نادرست وجود داشته باشند و تا زمانی که کاربر به ما گزارش ندهد از وجود آنها مطلع نخواهیم شد. چاره کار در چیزی به نام «انواع ناقص» (Partial Types) است.
انواع ناقص
انواع ناقص یکی از انواع کاربردی تایپ اسکریپت هستند که جایگزین مناسبی برای استفاده از Any محسوب میشوند. نوع ناقص میتواند شامل هر مشخصهای از نوع مبنا باشد، اما چیزی به جز آن نمیتواند داشته باشد.
این نخستین گام مطلوب است، چون ما میخواهیم هیچ غلط املایی در نامهای مشخصهها مداشته باشیم و همچنین نمیخواهیم نوع نادرستی را به متغیرها انتساب دهیم. اما فیلدهای مفقود را پوشش میدهد، چون انواع ناقص در واقع همه مشخصهها را اختیاری میکنند. بنابراین اگر درج const را برای یک رکورد فراموش کنیم، همچنان این مشکل را که مانند مثال زیر در زمان کامپایل نادیده گرفته میشود خواهیم داشت:
1interface Unit {
2 id: number;
3 type: string;
4 name: string;
5 cost: number;
6 experience: number;
7}
8
9const INFANTRY_DATA: Partial<Unit>[] = [{
10 name: 'Pikeman'
11 // Oh no, I'm missing a field here!
12},{
13 name: 'Rifleman',
14 cost: 10
15},{
16 name: 'Grenadier',
17 cost: 12
18}].map(unit => ({
19 ...unit,
20 type: 'Infantry'
21}));
22
23const CAVALRY_DATA: Partial<Unit>[] = [{
24 name: 'Light Cavalry',
25 cost: 16
26},{
27 name: 'Heavy Cavalry',
28 cost: 20
29}].map(unit => ({
30 ...unit,
31 type: 'Cavalry'
32}));
33
34export const UNIT_DATA: Unit[] = [...INFANTRY_DATA, ...CAVALRY_DATA].map((unit, i) => ({
35 ...unit,
36 id: i,
37 experience: 0
38} as Unit));
یک مشکل دیگر نیز وجود دارد. همچنان که گفتیم، Partial<Unit> موجب میشود که همه فیلدها در Unit به صورت اختیاری باشند. این بدان معنی است که دیگر نمیتوان آن را به Unit انتساب داد، زیرا نوعها متفاوت هستند. بنابراین در نهایت باید یک تبدیل مجدد به Unit داشته باشیم که میتواند به سادگی موجب پاک شدن رد خطاهایی شوند که میشد در زمان کامپایل شناسایی کرد. در نهایت میتوان هر چیزی را به نوع Unit تبدیل کرد و تایپ اسکریپت فرض میکند که ما میدانیم چه کاری انجام میدهیم.
Pick
Pick امکان تعیین یک نوع و انتخاب فیلدهایی که باید استفاده شوند را به صورت زیر فراهم میسازد:
let partialData: Pick<OriginalType, ‘field1’ | ‘field2’ | ‘field3’>;
بنابراین در مثال قبل، میتوانیم تعیین کنیم که کدام فیلدها را در نوع ناقص انتظار داریم:
1interface Unit {
2 id: number;
3 type: string;
4 name: string;
5 cost: number;
6 experience: number;
7}
8
9type PartialUnit = Pick<Unit, 'name' | 'cost'>;
10
11const INFANTRY_DATA: PartialUnit[] = [{
12 name: 'Pikeman',
13 cost: 8
14},{
15 name: 'Rifleman',
16 cost: 10
17},{
18 name: 'Grenadier',
19 cost: 12
20}].map(unit => ({
21 ...unit,
22 type: 'Infantry'
23}));
24
25const CAVALRY_DATA: PartialUnit[] = [{
26 name: 'Light Cavalry',
27 cost: 16
28},{
29 name: 'Heavy Cavalry',
30 cost: 20
31}].map(unit => ({
32 ...unit,
33 type: 'Cavalry'
34}));
35
36export const UNIT_DATA: Unit[] = [...INFANTRY_DATA, ...CAVALRY_DATA].map((unit, i) => ({
37 ...unit,
38 id: i,
39 experience: 0
40} as Unit));
حتی ممکن است متوجه شوید که میتوانیم موارد تکراری را با اعلان کردن یک باره نوع ناقص و سپس استفاده مجدد از آن در هر زیرمجموعه از دادهها کاهش دهیم. بدین ترتیب مجدداً نوعی امنیت نوع ایجاد میشود. اما این حالت در مثال کنونی دشوار است، زیرا با این که مثال ما چند فیلد دارد؛ اما در دادههای اصلی دهها فیلد داریم که تنها میخواهیم 2 یا 3 فیلد حذف شوند.
بنابراین اگر میشد به جای «انتخاب» (Pick) فیلدها آنها را صرفاً «نادیده» (Omit) بگیریم، بسیار بهتر بود.
Omit
Omit دقیقاً مخالف Pick است. در Pick فیلدهایی که میخواهیم نگه داریم را تعیین میکنیم، اما در Omit فیلدهایی که باید حذف شوند تعیین میشوند. در این حالت، میتوانیم دو فیلد را که میخواهیم حذف شوند تعیین کنیم تا بقیه باقی بمانند.
نوع ناقص حاصل چیزی مانند زیر خواهد بود:
type PartialUnit = Omit<Unit, ‘id’ | ‘experience’>;
سخن پایانی
بسیاری از افراد حتی از وجود چیزی به نام نوع ناقص اطلاع ندارند. اما این هم جزء همه آن ابزارهای جالبی است که تایپ اسکریپت در اختیار ما قرار داده است تا بهرهوری کد خود را بالا ببریم. در این مقاله سه مورد از این نوعها را مورد بررسی قرار دادیم، اما به شما پیشنهاد میکنیم بقیه موارد را نیز بررسی کنید تا بتوانید کدی گویا بنویسید، چون این هدف نهایی تایپ اسکریپت است.
اگر این مطلب برای شما مفید بوده است، آموزشهای زیر نیز به شما پیشنهاد میشوند:
- مجموعه آموزشهای JavaScript (جاوا اسکریپت)
- مجموعه آموزشهای برنامهنویسی
- راهنمای جامع تایپ اسکریپت (Typescript) — از صفر تا صد
- درک انواع در تایپ اسکریپت — به زبان ساده
- انواع پیشرفته در تایپ اسکریپت – از صفر تا صد
==