Menu

האופרטור satisfies ב-TypeScript: מול הערות טיפוס ו-as

האופרטור satisfies בודק שערך מתאים לטיפוס בלי לשנות את הטיפוס שהוסק לערך. למדו מה הוא עושה, איך הוא משתווה להערת טיפוס ול-as (אותו אובייקט שנכתב בשלוש דרכים), איך הוא משתלב עם as const, ולמה הוא מתאים לאובייקטי הגדרות.

בדף הזה יש עורכים שאפשר להריץ - לערוך, להריץ ולראות את הפלט מיד.

value satisfies Type בודק בזמן קומפילציה ש-value מתאים ל-Type, ואז משאיר את הטיפוס של הערך עצמו, המדויק יותר, בשקט. הערת טיפוס הייתה מחליפה את הטיפוס המדויק הזה ב-Type; satisfies מאמת בלי להרחיב.

satisfies עדיין מבצע את הבדיקה: צבע חסר, מפתח עם שגיאת כתיב כמו bleu, או ערך כמו true הם שגיאת קומפילציה בשורה הזו. הוא קיים מאז TypeScript 4.9, וכמו כל הערת טיפוס, הוא מוסר מה-JavaScript שנוצר.

הבעיה ש-satisfies פותר

עם הערת טיפוס, הטיפוס של המשתנה הוא ההערה. הקומפיילר שוכח מה הוא ראה בליטרל. כאן אותה פלטה מקבלת במקום זאת הערת טיפוס, ועכשיו TypeScript כבר לא יודע ש-green הוא מחרוזת:

הקומפיילר מדווח:

index.ts(11,27): error TS2339: Property 'toUpperCase' does not exist on type 'Color'.
  Property 'toUpperCase' does not exist on type '[number, number, number]'.

לפני TypeScript 4.9 האפשרויות היו: להוסיף הערת טיפוס ולצמצם ידנית בכל מקום (typeof palette.green === "string"), או לוותר על ההערה ולאבד את הבדיקה. satisfies נותן את שניהם. שנו את : Record<ColorName, Color> ל-satisfies Record<ColorName, Color> אחרי הסוגר המסולסל הסוגר, והקוד ירוץ.

satisfies מול הערת טיפוס מול as

אותו אובייקט הגדרות, שנכתב בשלוש דרכים:

as נתן ל-lang החסר לעבור, ו-asserted.lang הוא undefined בזמן ריצה בזמן שהטיפוס שלו אומר string. הסירו את lang משתי השורות האחרות ושתיהן ייכשלו עם TS2741, Property 'lang' is missing in type ....

הערת טיפוס const x: T = vAssertion v as Tv satisfies T
מאפיינים חסריםשגיאהמותרשגיאה
מאפיינים עודפים (ליטרל אובייקט)שגיאהמותרשגיאה
טיפוס מאפיין שגוישגיאהרק אם הטיפוסים לא חופפיםשגיאה
הטיפוס של x אחר כךTTהטיפוס שהוסק ל-v
טיפוסים ליטרליים ("dark", 8080)מורחבים ל-Tמורחבים ל-Tנשמרים כש-T מתיר אותם
מפתחות של Record<string, ...>כל מחרוזת (שגיאות כתיב עוברות)כל מחרוזתבדיוק המפתחות שנכתבו
השפעה בזמן ריצהאיןאיןאין

כלל אצבע: הוסיפו הערת טיפוס כשאתם רוצים שלמשתנה יהיה הטיפוס המוצהר (ערך שתבצעו לו השמה מחדש, API ציבורי), והשתמשו ב-satisfies כשאתם רוצים בדיקה, אבל הטיפוס של הערך עצמו שימושי יותר.

תפיסת טעויות בליטרלי אובייקט

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

type Route = { path: string; method: "GET" | "POST" };

const home = { path: "/", metod: "GET" } satisfies Route;
// error TS2561: Object literal may only specify known properties, but 'metod' does not exist in type 'Route'. Did you mean to write 'method'?

הבדיקה גם נותנת לליטרל טיפוס הקשרי (contextual type), בדיוק כמו הערת טיפוס. זה חשוב בשתי דרכים. ליטרלים של מחרוזת נשמרים כטיפוסים ליטרליים כשטיפוס היעד מצפה להם: ל-{ path: "/", method: "GET" } satisfies Route יש method: "GET", בזמן שלאותו אובייקט בלי הערת טיפוס היה מוסק method: string. ופרמטרים של callbacks מוסקים מטיפוס היעד:

המפתחות של Record נשארים ידועים

שימוש נפוץ הוא טבלת חיפוש. עם הערת טיפוס Record<string, T>, כל מחרוזת היא מפתח תקין ושגיאת כתיב עוברת קומפילציה ומחזירה undefined בזמן ריצה. עם satisfies, הערכים עדיין נבדקים מול T, אבל הטיפוס של המשתנה מפרט בדיוק את המפתחות שכתבתם:

keyof typeof endpoints שימושי רק כי המפתחות שרדו. עם הערת הטיפוס הוא היה פשוט string.

כדי לדרוש קבוצה קבועה של מפתחות, השתמשו ב-satisfies מול Record על איחוד: satisfies Record<"dev" | "prod", string> מדווח על prod חסר עם TS2741 ועל staging לא מוכר עם TS2353.

as const satisfies

as const ו-satisfies משתלבים. כתבו את as const קודם: הוא הופך את הערך ל-readonly לעומק עם טיפוסים ליטרליים, ואז satisfies בודק את הערך המדויק הזה.

כל נתיב נבדק מול Route (method: "PUT" היה שגיאה), וה-tuple של הטיפוסים הליטרליים נשאר זמין, ולכן Path הוא איחוד של הנתיבים האמיתיים. השתמשו ב-readonly Route[] (או ב-ReadonlyArray<Route>) כיעד, כי מערך as const הוא לקריאה בלבד.

אובייקטי הגדרות

הגדרות הן המקום שבו satisfies מוכיח את עצמו: הצורה חייבת להיות נכונה, וקוד במקומות אחרים רוצה את הערכים המדויקים.

שכחו את הרשומה production, כתבו את logLevel עם שגיאת כתיב, או כתבו logLevel: "verbose", והקומפיילר יצביע על השורה המדויקת. אותו דפוס מתאים לקובצי *.config.ts: export default { ... } satisfies SomeConfig בודק את הקובץ כולו, בזמן שהאובייקט המיוצא שומר על הערכים הליטרליים שלו.

מתי לא להשתמש ב-satisfies

  • המשתנה יקבל השמה מחדש. let cfg = { port: 3000 } satisfies { port: number | string } נותן ל-cfg את הטיפוס { port: number }, ולכן cfg = { port: "80" } מאוחר יותר נכשל (TS2322). הוסיפו הערת טיפוס למשתנים שאתם מתכוונים לשנות.
  • אתם רוצים את הטיפוס המוצהר בכוונה. לערך החזרה של פונקציה או לקבוע מיוצא שהוא חלק מ-API, הטיפוס של ההערה הוא החוזה, וחשיפת הטיפוס הליטרלי המדויק עלולה להפוך שינויים עתידיים לשוברים.
  • הערך אינו ליטרל. satisfies בולט על ליטרלים של אובייקטים ומערכים. על משתנה או על תוצאה של קריאה הוא בדיקת שיוך פשוטה, שהערת טיפוס כבר נותנת לכם.

שאלות נפוצות

מה satisfies עושה ב-TypeScript?

expression satisfies Type בודק בזמן קומפילציה שאפשר לשייך את הביטוי ל-Type, מדווח על מאפיינים חסרים, מאפיינים עודפים וטיפוסי ערך שגויים, ואז משאיר את הטיפוס שהוסק לביטוי עצמו בלי שינוי. מקבלים את הבטיחות של הערת טיפוס ואת הדיוק של הסקת טיפוסים. הוא נמחק מפלט ה-JavaScript.

מה ההבדל בין satisfies להערת טיפוס?

שניהם בודקים את הערך. הערת טיפוס (const x: T = ...) נותנת אחר כך למשתנה את הטיפוס T, ושוכחת את מה שהקומפיילר ידע על הערך (טיפוסים ליטרליים, לאיזה איבר באיחוד שייך כל מאפיין, אילו מפתחות קיימים). satisfies T שומר על הטיפוס שהוסק, ולכן ידוע ש-x.someKey קיים, ומאפיין string | number שמחזיק מחרוזת מקבל את הטיפוס string.

מה ההבדל בין satisfies ל-as ב-TypeScript?

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

מה המשמעות של as const satisfies?

הוא מחיל את שניהם: as const הופך את הערך ללקריאה בלבד לעומק עם טיפוסים ליטרליים, ואז satisfies בודק את התוצאה הזו מול טיפוס. כתבו את as const קודם: const routes = [...] as const satisfies readonly Route[];. המשתנה שומר על הטיפוסים הליטרליים המדויקים לשימוש בהמשך, ורשומה שגויה היא עדיין שגיאת קומפילציה.

באיזו גרסה של TypeScript נוסף satisfies?

TypeScript 4.9, שיצאה בנובמבר 2022. זה תחביר פשוט שאפשר למחוק, ולכן הוא רץ גם תחת ה-type stripping המובנה של Node, וכל גרסה עדכנית של TypeScript (כולל 7) תומכת בו.

איור של שפות התכנות ב-Coddy

ללמוד תכנות עם Coddy

להתחיל