סוגים ממותגים ב-TypeScript

כאשר אתם ממדלים ישויות עם TypeScript, זה מאוד נפוץ לקבל ממשק כמו זה:

TypeScript

 

הבעיה

סוגי התכונות אין להם משמעות סמנטית. במונחים של סוגים, User.id, Order.id, Order.year וכו' הם אותו הדבר: מספר, וכמספר הם ניתנים להחלפה, אך סמנטית, הם לא.

בהמשך לדוגמה הקודמת, אנו יכולים לקבל סט של פונקציות שמבצעות פעולות על הישויות. לדוגמה:

TypeScript

 

הפונקציות הללו יקבלו כל מספר בכל ארגומנט לא משנה מה המשמעות הסמנטית של המספר. לדוגמה:

TypeScript

 

ברור שזה טעות גדולה, וזה עשוי להיראות קל להימנע מקריאת הקוד, אבל הקוד לא תמיד פשוט כמו בדוגמה.

אותו הדבר קורה עם getOrdersFiltered: אנו יכולים להחליף את הערכים של יום וחודש, ולא נקבל שום אזהרה או שגיאה. השגיאות יתרחשו אם היום גדול מ-12, אבל ברור שהתוצאה לא תהיה כמו שציפינו.

הפתרון

כללי הקליסטניקה של אובייקטים מספקים פתרון לכך: לעטוף את כל הפרימיטיבים ומחרוזות (הקשר של הפרימיטיב הוא אנטי-תבנית). הכלל הוא לעטוף את הפרימיטיבים באובייקט שמייצג משמעות סמנטית (DDD מתאר את זה כValueObjects).

אבל עם TypeScript, אנחנו לא צריכים להשתמש במחלקות או אובייקטים לכך: אנחנו יכולים להשתמש במערכת הסוגים כדי להבטיח שמספר המייצג משהו שונה משנה לא יכול לשמש במקום שנה.

סוגים ממותגים

תבנית זו עושה שימוש בהרחבה של סוגים כדי להוסיף מאפיין שמבטיח את המשמעות הסמנטית:

TypeScript

 

השורה הפשוטה הזו יוצרת סוג חדש שיכול לפעול כמספר — אבל לא מספר, זהו שנה.

TypeScript

 

כלליזציה של הפתרון

כדי למנוע כתיבת סוג עבור כל סוג מותג, אנו יכולים ליצור סוג עזר כמו:

TypeScript

 

שמשתמש בסמל ייחודי כשם מאפיין המותג כדי להימנע מעימותים עם המאפיינים שלך ומקבל את הסוג המקורי ואת המותג כפרמטרים גנריים.

עם זאת, אנו יכולים לבצע ריפרפ של המודלים והפונקציות שלנו כך:

TypeScript

 

עכשיו, בדוגמה הזו, ה-IDE יראה שגיאה מכיוון שid הוא UserId וdeleteOrder מצפה לOrderId.

TypeScript

 

החזרות

כפיצוי קטן, תצטרך להשתמש בX כBrand. לדוגמה, const year = 2012 as Year כאשר אתה יוצר ערך חדש מתוך פרימיטיב, אבל זה שווה ערך לnew Year(2012) אם אתה משתמש באובייקטי ערך. תוכל לספק פונקציה שעובדת כסוג של "קונסטרקטור":

TypeScript

 

אימות עם סוגים ממותגים

סוגים ממותגים שימושיים גם כדי להבטיח שהנתונים תקינים מכיוון שניתן שיהיו סוגים ספציפיים לנתונים מאומתים, ואתה יכול לסמוך על שהמשתמש אומת רק על ידי שימוש בסוגים:

TypeScript

 

Readonly אינו חובה, אבל כדי להיות בטוח שקוד שלך לא ישנה את הנתונים לאחר אימותם, מאוד מומלץ.

סיכום

סוגים ממותגים הם פתרון פשוט שכולל את הדברים הבאים:

  • משפר את קריאות הקוד: מבהיר יותר איזה ערך צריך להשתמש בכל ארגומנט
  • אמינות: עוזרת להימנע מטעויות בקוד שיכולות להיות קשות לזיהוי; עכשיו ה-IDE (והבדיקות סוגים) עוזרים לנו לגלות אם הערך נמצא במקום הנכון
  • אימות נתונים: ניתן להשתמש בסוגים ממותגים כדי להבטיח שהנתונים תקינים.

אפשר לחשוב על סוגים ממותגים כסוג של גרסה של ValueObjects אבל בלי להשתמש במחלקות — רק סוגים ופונקציות.

תהנו מכוח הטייפים!

Source:
https://dzone.com/articles/branded-types-in-typescript