• בלוג
  • למה שמישהו ירצה להיפרד מטייפסקריפט?

למה שמישהו ירצה להיפרד מטייפסקריפט?

11/09/2023

הויכוח ברוב הרשתות בימים האחרונים סביב ההודעה של DHH על פרידה מטייפסקריפט בפרויקט Turbo8 הציג את הסיפור בתור בחירה פשוטה בין "כתיבת קוד מהיר" ל"כתיבת קוד איכותי", והיה מאוד מפתה לשים את DHH במשבצת ה"כתיבת קוד מהיר". אבל לפני שארגיש בנוח עם החלוקה הזאת, יהיה מעניין להיכנס ל PR הרלוונטי ולראות מה זה בעצם אומר להיפרד מ TypeScript ולמה DHH היה כל כך נחוש.

כאן אפשר למצוא את כל הקבצים שהשתנו: https://github.com/hotwired/turbo/pull/971/files.

בואו ננסה למצוא כמה מוקדים מעניינים-

1. טייפסקריפט הוסיף סמנטיקה לשפה

בקובץ form_submission.ts אני מוצא את הבלוק:

export type FormSubmissionResult = { success: boolean; fetchResponse: FetchResponse } | { success: false; error: Error }

export enum FormSubmissionState {
  initialized,
  requesting,
  waiting,
  receiving,
  stopping,
  stopped,
}

enum FormEnctype {
  urlEncoded = "application/x-www-form-urlencoded",
  multipart = "multipart/form-data",
  plain = "text/plain",
}

שב JavaScript הופך ל:

export const FormSubmissionState = {
  initialized: "initialized",
  requesting: "requesting",
  waiting: "waiting",
  receiving: "receiving",
  stopping: "stopping",
  stopped: "stopped"
}

מתכנתי JavaScript שצריכים לתחזק את הקוד עשויים להיתקל במילים חדשות כמו enum ולתהות בשביל מה צריך את זה. הבטחתם לנו "רק" מערכת טיפוסים, וקיבלנו מילים חדשות עם משמעות חדשה שעכשיו צריך להכיר.

דוגמה נוספת מהקובץ cache.ts היא הבלוק הזה:

resetCacheControl() {
    this.setCacheControl("")
  }

  exemptPageFromCache() {
    this.setCacheControl("no-cache")
  }

  exemptPageFromPreview() {
    this.setCacheControl("no-preview")
  }

  private setCacheControl(value: string) {
    setMetaContent("turbo-cache-control", value)
  }
}

שהופך ב JavaScript ל:

 resetCacheControl() {
    this.#setCacheControl("")
  }

  exemptPageFromCache() {
    this.#setCacheControl("no-cache")
  }

  exemptPageFromPreview() {
    this.#setCacheControl("no-preview")
  }

  #setCacheControl(value) {
    setMetaContent("turbo-cache-control", value)
  }
}

ושוב אנחנו לוקחים יכולת שכבר קיימת ב JavaScript, משנים לה את התחביר והסמנטיקה ובונים משהו שמתכנתי JavaScript אולי יצטרכו לעבוד בשביל להבין.

2. לשמח את טייפסקריפט

בקובץ form_submission.ts אני מוצא את הבלוק:

  get stringFormData() {
    return [...this.formData].reduce((entries, [name, value]) => {
      return entries.concat(typeof value == "string" ? [[name, value]] : [])
    }, [] as [string, string][])
  }

שורת ה as בסוף הבלוק נמצאת שם רק בשביל לשמח את טייפסקריפט. היא לא מוסיפה ערך למתכנתים ובטח שלא למתכנתים שיצטרכו להשתמש בספריה. אפילו טייפסקריפט יודע שאם היינו מוסיפים שם ts-ignore במקום ה as הכל היה פשוט עובד. הצורך להוסיף מבנים מיוחדים כמו as לקוד רק בשביל לשמח קומפיילר יכול להיות בעייתי כשרוב המתכנתים בפרויקט הם אנשי JavaScript.

3. טיפוסים לפעמים מסתירים טעויות בקוד

שימו לב לאחת ההערות מאותו PR:

This one is interesting. I would have no idea what a "submitter's" purpose is here with or without Typescript.

I am excited to see how this code base changes without Typescript. The "types" or what things are will have to be communicated in some way. This may lead to very readable code.

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

אישית גם אחרי קריאה של ה PR לא השתכנעתי שההחלטה לוותר על טייפסקריפט היתה נכונה. ברור שזה יחסוך קצת זמן פיתוח, אבל העלות בריפקטורינג מסובך יותר תהיה מורגשת ובלי Type Hints יהיה יותר קשה למפתחים מחוץ לפרויקט לתרום ולהרגיש בבית. ועדיין אי אפשר להגיד שלטייפסקריפט אין עלות או שזו רק מערכים טיפוסים שלא משנה את האופי של הקוד.

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