برنامه نویسی

استفاده از انواع نگاشت در 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 بسازیم

اجازه دهید ابتدا آنچه را که می خواهیم در اینجا تعریف کنیم. ما می خواهیم ابزاری برای:

  1. باید آرگومان دوم را بپذیرد که به ویژگی ها می گوید بر اساس نوع حذف شوند.
  2. از هر ویژگی نوع عبور کنید
  3. اگر مقدار ویژگی برابر با آرگومان دوم باشد، باید آن را حذف کند

اما قبل از شروع اجرای خود، باید نگاهی به نحوه اصلی بیاندازیم 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 به عنوان دومین پارامتری که باید حذف شود. اما در اینجا ما کارهای زیر را انجام می دهیم:

  1. از آنجایی که ما در حال پذیرش نوع حذف است DataType در ما OmitByType ابزار، ما آن را با مقدار ویژگی فعلی مقایسه می کنیم. در اینجا ویژگی فعلی است Type[K]
  2. اگر با 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.

نوشته های مشابه

دیدگاهتان را بنویسید

نشانی ایمیل شما منتشر نخواهد شد. بخش‌های موردنیاز علامت‌گذاری شده‌اند *

دکمه بازگشت به بالا