استفاده از انواع نگاشت در TypeScript: کاربردهای عملی

معرفی
زمانی که من به تازگی با تایپ اسکریپت آشنا شده بودم، رابط های اولیه و انواع شی را می نوشتم. من به دلیل این گونه ها توجه چندانی نکردید و از منظر مقیاس پذیری به آن فکر نکردید.
بعداً تصمیم گرفتم در بازی TS سطح خودم را بالا ببرم. بنابراین من با چالشهای تایپنویسی شروع کردم، جایی که با مسائل آسان شروع کردم و سپس به سراغ مسائل پیچیده رفتم. چالش ها به خوبی ساختار یافته اند تا بتوانید اصول اولیه را یاد بگیرید. اما چیزی که بیشتر به من کمک کرد تجزیه و تحلیل راه حل افراد دیگر در Github Issues و کانال یوتیوب Michgan Typescript بود. این موضوع درک من از Typescript را 10 برابر افزایش داد.
این چالش ها به من انگیزه داده تا کدی بنویسم که عبارتند از:
- مقیاس پذیر
- ساختار یافته
- Typesafe
- کمتر مستعد خطا
من شروع به درک تایپ اسکریپت به روشی عمیق تر کردم. همه این چالش ها به من انگیزه داده است که این پست وبلاگ را در مورد انواع نقشه برداری شده در Typescript بنویسم. بنابراین در این وبلاگ، من قصد دارم به طور خلاصه در مورد نوع تایپ اسکریپت Mapped صحبت کنم و کاربردهای واقعی آن را به طور مفصل توضیح دهم.
💡 توجه داشته باشید: این کاربرد انواع نگاشت شده را می توان در چالش های تایپ اسکریپت به اشتراک گذاشته شده در بالا یافت.
Mapped Types در Typescript چیست؟
انواع نقشهبرداری شده در TS چیزی نیست جز راهی که از طریق آن میتوانید ویژگیهای نوع شیء تایپاسکریپ را طی کنید و آنها را در صورت نیاز اصلاح کنید.
برای نقل قول از cheatsheet TS:
Mapped Types مانند یک دستور نقشه برای سیستم نوع عمل می کند و به ورودی اجازه می دهد تا ساختار نوع جدید را تغییر دهد.
نوع زیر را در نظر بگیرید:
type Vehicle = {
type: 'Car',
noOfWheels: 4,
color: 'white'
}
حال تصور کنید که باید یک نوع جدید از the ایجاد کنید Vehicle
طوری تایپ کنید که تمام پارامترها اختیاری باشند. در اینجا می توان از انواع نقشه برداری شده برای دریافت موثر این نیاز درست مانند زیر استفاده کرد:
type Car = {
[P in keyof Vehicle]?: Vehicle[P]
}
این همه ویژگی هایی را که اختیاری هستند ایجاد می کند. این یک مورد استفاده ساده است، اما انواع نقشهبرداری شده میتوانند کارهای بسیار هیجانانگیزی انجام دهند. میتوانید نحوه تغییر ویژگیهای موجود در نگاشت تایپشده در این بخش از اسناد TS را بخوانید: https://www.typescriptlang.org/docs/handbook/2/mapped-types.html#mapping-modifiers
با این درک از انواع نقشهبرداری شده، اجازه دهید با برخی از موارد استفاده در زندگی واقعی شروع کنیم
مورد استفاده شماره 1: OmitByType
اگر شما یک توسعه دهنده تایپ اسکریپت هستید، مطمئناً از این ابزار TS به نام Omit استفاده کرده اید. این به شما کمک می کند تا با حذف کلیدهای ذکر شده در پارامتر دوم، یک نوع جدید از نوع موجود بسازید. Omit
.
نگاهی به ما بیندازید Vehicle
مثال با Omit
سودمندی:
type Vehicle = {
type: 'Car',
noOfWheels: 4,
color: 'white'
}
// Let us Omit noOfWheels
type OmmitedVehicle = Omit<Vehicle, 'noOfWheels'>
/*
type OmmitedVehicle = {
type: 'Car',
color: 'white'
}
*/
در اینجا ما در حال حذف ملک هستیم noOfWheels
از جانب Vehicle
یعنی ما در حال حذف خواص بر اساس نام ویژگی هستیم. اگر بخواهیم یک ویژگی را بر اساس نوعی که به عنوان آرگومان دوم به آن پاس می دهیم حذف کنیم، چه می شود Omit
به عنوان مثال، ما می خواهیم تمام ویژگی ها را از آن حذف کنیم Vehicle
مقادیر who’s string هستند یعنی حذف type
و color
ویژگی.
در حال حاضر TS هیچ گونه ابزاری برای حذف توسط Type ندارد. اما ما می توانیم این را به راحتی با کمک انواع Mapped و Generics بسازیم
اجازه دهید ابتدا آنچه را که می خواهیم در اینجا تعریف کنیم. ما می خواهیم ابزاری برای:
- باید آرگومان دوم را بپذیرد که به ویژگی ها می گوید بر اساس نوع حذف شوند.
- از هر ویژگی نوع عبور کنید
- اگر مقدار ویژگی برابر با آرگومان دوم باشد، باید آن را حذف کند
اما قبل از شروع اجرای خود، باید نگاهی به نحوه اصلی بیاندازیم Omit
نوع کاربردی:
type Omit<T, U> = {
[K in keyof T as Exclude<K, U>]: T[K]
}
ابتدا از هر کلید عبور می کند T
تایپ کنید با کمک:
K in keyof T
از نظر ما Vehicle
به عنوان مثال، K در اینجا نام ویژگی است که نوع نقشه برداری شده از آن عبور می کند type
noOfWheels
color
. بعد، با کمک as
بند ما کلید را مجدداً ترسیم می کنیم به طوری که اگر کلید K با پارامتر دوم مطابقت داشته باشد حذف می کنیم. Omit
سودمند. دوباره اگر عملکرد داخلی را ببینید Exclude
ابزار ارائه شده توسط TS به شرح زیر است:
type Exclude<T, U> = T extends U ? never : T
این بدان معناست که اگر T
و U
مطابقت دارد سپس برمی گردد never
یا در غیر این صورت نوع را برمی گرداند T
. این همچنین به این معنی است که در مسابقه هر دو نوع ما نمی خواهیم چیزی را که هست برگردانیم never
. این مفید می شود در Omit
مورد به شرح زیر
اگر کلید K
در نوع نگاشت فوق با پارامتر دوم یعنی مطابقت دارد U
پس نباید آن را در نوع نگاشت قرار دهیم. این مورد را می توان به صورت زیر تجسم کرد:
type Vehicle = {
type: 'Car',
noOfWheels: 4,
color: 'white'
}
type OmmitedVehicle = Omit<Vehicle, 'noOfWheels'>
/*
Internally this happens:
type OmittedVehicle = {
}
which truns into:
type OmittedVehicle = {
}
and a key in mapped type that is mapped to never becomes excluded from the mapping.
Ideally this would return a blank object like `{}`
*/
بنابراین این یک نوع جدید می دهد که با حذف ویژگی های نام ساخته شده است. دریافت این درک مهم بود تا بتوانید به راحتی مفهوم ابزار ما را درک کنید: OmitByType
. در اینجا ابزار مفید است:
type OmitByType<Type, DataType> = {
[K in keyof Type as Exclude<K, Type[K] extends DataType ? K : never>]: Type[K]
}
فقط این نوع ابزار را با دقت تماشا کنید. خیلی شبیه اصلش نیست Omit
نوع بله، در واقع اینطور است، اما یک تغییر کوچک وجود دارد که در پارامتر دوم Exclude است:
Type[K] extends DataType ? K : never
پیش از این در Omit
ما مستقیماً تایپ می کنیم U
به عنوان دومین پارامتری که باید حذف شود. اما در اینجا ما کارهای زیر را انجام می دهیم:
- از آنجایی که ما در حال پذیرش نوع حذف است
DataType
در ماOmitByType
ابزار، ما آن را با مقدار ویژگی فعلی مقایسه می کنیم. در اینجا ویژگی فعلی استType[K]
- اگر با
DataType
ما این ویژگی K را به exclude منتقل می کنیم وگرنه هرگز برنمی گردیم.
با انجام خشک کار همه چیز روشن تر می شود. برای اجرای خشک اجازه دهید ما دوباره استفاده کنیم Vehicle
مثال از بالا:
type Vehicle = {
type: 'Car',
noOfWheels: 4,
color: 'white'
}
type OmitByType<Type, DataType> = {
[K in keyof Type as Exclude<K, Type[K] extends DataType ? K : never>]: Type[K]
}
type OmittedCarByType = OmitByType<Vehicle, string>
برای کد بالا، اجرای خشک به شکل زیر خواهد بود:
کلید | ارزش | نوع ارزش | بررسی وضعیت (نوع مقدار رشته را گسترش می دهد؟) | مستثنی کردن | عمل |
---|---|---|---|---|---|
نوع | ‘ماشین’ | رشته | آره | نوع را حذف کنید | نوع را حذف کنید |
noOfWheels | 4 | عدد | خیر | شامل noOfWheels | شامل noOfWheels |
رنگ | ‘سفید’ | رشته | آره | رنگ را حذف کنید | رنگ را حذف کنید |
این نوع ابزار راه حلی برای چالش تایپ اسکریپت زیر بود: https://github.com/type-challenges/type-challenges/blob/main/questions/02852-medium-omitbytype/README.md
Usecase #2: PartialByKeys
این ابزار دوباره بسیار شبیه به ابزار Omit است. PartialByKeys
آرگومان دوم را به عنوان اتحاد کلیدها می گیرد که باید در آرگومان Type T بخشی یا اختیاری شود.
در اینجا نحوه ظاهر این ابزار به شرح زیر است:
type Flatten<T> = {
[K in keyof T]: T[K]
}
type PartialByKeys<T extends {}, K extends keyof T = keyof T> = [K] extends [''] ? Partial<T> : Flatten<Omit<T, K> & Partial<Pick<T, K>>>
اجازه دهید این را کمی ساده کنم و کارکرد آن را برای شما توضیح دهم:
- در اینجا ما یک ابزار عمومی به نام ایجاد کرده ایم
PartialByKeys
که در دو استدلالT
وk
.T
انتظار می رود از نوع شی وK
انتظار می رود از کلیدهایT
و با کلیدهای مقداردهی اولیه می شودT
نوع شی -
سپس، ابتدا بررسی می کنیم که آیا Keys
K
خالی هستند یا نه اگر اینطور است، باید یک نوع شی را برگردانیم که همه کلیدها اختیاری هستند.- به این نحوی که در اینجا استفاده می شود توجه کنید:
[K] extends ['']
-
اگر
K
کلیدها خالی نیستند، پس باید یک نوع شی جدید با کلیدهای مشخص شده به صورت اختیاری/جزئی برگردانیم.- در اینجا ما یک ترفند هوشمندانه را با جدا کردن کلیدهایی که برای جزئی بودن لازم نیست انجام می دهیم و با کمک آن نگه می داریم
Omit
-
مواردی را که می خواهیم جزئی کنیم ابتدا یک نوع شی جدید با کلیدهایی که باید جزئی باشند می سازیم. ما این کار را با کمک انجام می دهیم
Pick
.Pick<T, K>
-
بعد من کل این انتخاب جزئی با کمک
Partial
.> -
در نهایت ما این هر دو شی را در یکی ترکیب می کنیم.
-
نکته سریع: ما همچنین از ابزار Flatten استفاده می کنیم تا بتوانید به جای کلیدهای انتخابی + قسمتی بهم ریخته، یک حاشیه نویسی واضح دریافت کنید.
- در اینجا ما یک ترفند هوشمندانه را با جدا کردن کلیدهایی که برای جزئی بودن لازم نیست انجام می دهیم و با کمک آن نگه می داریم
در اینجا اجرای خشک این ابزار با Vehicle
مثال:
کلید | ارزش | در Omit گنجانده شده است؟ | شامل جزئی>؟ | گنجاندن نهایی | اختیاری؟ |
---|---|---|---|---|---|
نوع | ‘ماشین’ | آره | خیر | آره | خیر |
noOfWheels | 4 | آره | خیر | آره | خیر |
رنگ | ‘سفید’ | خیر | آره | آره | آره |
مورد استفاده شماره 3: PickByType
که در PickByType
، تمام ویژگی های نوع شی را انتخاب می کنیم که مقدار آن با نوع مشخص شده در ابزار به عنوان آرگومان دوم مطابقت دارد.
در اینجا چگونه است PickByType
به نظر می رسد:
type PickByType<T, U> = {
[P in keyof T as T[P] extends U ? P : never]: T[P]
}
تنها چیزی که این را با OmitByType
راهی است که ما از آن استفاده می کنیم as
عبارت. در اینجا ما از آن استفاده می کنیم as
عبارتی به گونهای که فقط کلیدها نشان داده میشوند که چه کسی ارزش دارد T[P]
مطابقت با U
یعنی پارامتر دوم
در اینجا اجرای خشک این ابزار بر روی است Vehicle
نوع:
کلید | ارزش | نوع ارزش | رشته کبریت؟ | شامل کلید در نتیجه؟ |
---|---|---|---|---|
نوع | ‘ماشین’ | رشته | آره | آره |
noOfWheels | 4 | عدد | خیر | خیر |
رنگ | ‘سفید’ | رشته | آره | آره |
خلاصه
در ابتدا ممکن است درک، پیروی از حروف تایپی دیوانه کننده به نظر برسد و ممکن است مانند یک جادوگری به نظر برسد. اما به من اعتماد کنید وقتی اصول اولیه را یاد بگیرید و شروع به تمرین کنید ساده تر می شود.
بنابراین در این پست وبلاگ ما در مورد انواع Mapped و موارد استفاده دیوانه وار آن آشنا شدیم. ما همچنین برخی از عملکردهای داخلی انواع ابزار TS موجود مانند Omit
و Exclude
.
از اینکه پست وبلاگ من را خواندید بسیار متشکرم.
شما می توانید من را دنبال کنید توییتر، github و linkedIn.