• בלוג
  • TypeScript
  • [טייפסקריפט] דוגמה טובה לשימוש ב Function Overloading

[טייפסקריפט] דוגמה טובה לשימוש ב Function Overloading

17/12/2022

פיצ'ר Function Overloading של טייפסקריפט היה יכול להיות ממש מוצלח - הוא היה יכול להבין לפי הטיפוסים של הפרמטרים בזמן קומפילציה לאיזו גירסה של פונקציה התכוונו ולהפעיל אותה, במקום להכריח אותנו לכתוב גירסה כללית של הפונקציה בסגנון JavaScript. אבל למרות המימוש הקצת לוקה בחסר שלו, הוא עדיין יכול לעזור לנו בחיים האמיתיים לכתוב פונקציות עזר שיהיה כיף להפעיל. אם רק נקפיד לעבוד נכון.

1. מה אנחנו בונים

אני רוצה לכתוב פונקציה בשם css שמקבלת אלמנט DOM בתור פרמטר ראשון, ועכשיו יש שתי אפשרויות:

  1. הפרמטר השני יכול להיות מאפיין CSS, ואז הפרמטר השלישי יהיה הערך של אותו מאפיין.

  2. הפרמטר השני יכול להיות אוביקט, שהמפתחות בו יהיו מאפייני CSS והערכים יהיו הערכים שמתאימים להם.

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

const el = document.querySelector('h1')!;

css(el, 'fontSize', '16px');
css(el, { background: 'red', padding: '10px' });

אבל הפעלות כאלה לא יתקמפלו:

// foo is not a valid CSS attribute name
css(el, 'foo', 'bar');

// 17 is not a valid value for 'background'
css(el, 'background', 17);

// no need for the third parameter
css(el, { background: 'red' }, 'red')

2. מימוש הפונקציה

בגלל ש css צריכה לתמוך בשתי חתימות, אני מעדיף לכתוב פונקציית עזר (או פונקציות עזר אם יש צורך) שיממשו כל חתימה בנפרד. במקרה שלנו פונקציית עזר אחת תספיק, כי מאוד קל לראות איך לתרגם את הפמטרים מהחתימה של מאפיין יחיד לחתימת האוביקט. לכן אני מתחיל בכתיבת הפונקציה שמקבלת אלמנט ואוביקט |CSS ומעדכנת את כל הגדרות העיצוב:

function cssSetAttributes(el: HTMLElement, css: Partial<CSSStyleDeclaration>) {
  Object.assign(el.style, css);
}

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

function css<T extends keyof CSSStyleDeclaration>(el: HTMLElement, attr: T, value: CSSStyleDeclaration[T]): void;
function css(el: HTMLElement, css: Partial<CSSStyleDeclaration>): void;
function css(el: HTMLElement, cssOrAttr: any, value?: any) {
  if (value) {
    cssSetAttributes(el, { [cssOrAttr]: value });
  } else {
    cssSetAttributes(el, cssOrAttr);
  }
}

חלוקת תפקידים ברורה בין פונקציית ה dispatch (זאת שכל החתימות מתנקזות אליה, והיא צריכה להחליט מה לעשות) לבין המימוש או המימושים השונים, היא המפתח לכתיבת Function Overloading יעיל בטייפסקריפט.

מוזמנים לראות את הקוד המלא בפעולה ולשחק איתו בקודפן בקישור: https://codepen.io/ynonp/pen/zYLONxe?editors=1010 או ממש כאן מוטמע: