Menu

Literal Types ו-as const ב-TypeScript, עם דוגמאות

literal type הוא טיפוס עם ערך אחד בדיוק, כמו "GET" או 404. כאן תלמדו literal types של string, number ו-boolean, unions של literals, למה let מרחיב ו-const לא, מה as const עושה, ו-const type parameters.

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

literal type הוא טיפוס עם ערך אחד בדיוק: "up" הוא טיפוס שהאיבר היחיד שלו הוא המחרוזת "up", ו-404 הוא טיפוס שהאיבר היחיד שלו הוא המספר 404. לבדם הם לא שימושיים במיוחד. כשמחברים אותם ל-union, מקבלים משתנה שמקבל קבוצה קבועה של ערכים ותו לא.

בלי ההערה @ts-expect-error, הקריאה האחרונה היא שגיאה בזמן קומפילציה (TS2345). איתה, התוכנית מתקמפלת, והקריאה עדיין רצה ומדפיסה moving north by 1: literal types קיימים רק בשביל הקומפיילר, ובזמן ריצה הערך הוא מחרוזת רגילה.

Literals של String, Number ו-Boolean

כל ערך של string, number, bigint או boolean אפשר לכתוב כטיפוס. הקומפיילר יקבל אז רק את הערך המדויק הזה.

let method: "GET" = "GET";
let code: 404 = 404;
let ok: true = true;
let big: 10n = 10n;

type Port = 80 | 443 | 8080;
const port: Port = 443;

boolean עצמו הוא פשוט ה-union true | false, ולכן צמצום של boolean עם if (flag) משאיר את false בענף ה-else.

Literal typeמאפשרטיפוס רחב יותר
"GET"רק את המחרוזת "GET"string
404רק את המספר 404number
10nרק את ה-bigint 10bigint
trueרק את trueboolean

Unions של Literals

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

union של string literals הוא החלופה המקובלת ל-enum ב-TypeScript. הוא לא עולה כלום בזמן ריצה, הערכים הם מחרוזות רגילות שאפשר להדפיס ללוג ולשלוח ב-JSON, ושגיאת הקלדה היא שגיאת קומפילציה. עוד על היתרונות והחסרונות ב-enums.

Widening: let מול const

כש-TypeScript מסיק טיפוס מ-literal, הוא בודק אם הערך יכול להשתנות. אי אפשר אף פעם להשים ערך חדש למשתנה const, ולכן הוא שומר על ה-literal type. למשתנה let אפשר, ולכן הטיפוס שלו מורחב לטיפוס הכללי.

רחפו עם העכבר מעל כל שם בעורך כדי לראות את הטיפוס שהוסק. אם אתם רוצים let שמכיל רק ערכים מסוימים, תנו לו annotation: let mode: "light" | "dark" = "light".

למה מאפייני אובייקט מתרחבים

מאפיינים של object literal ניתנים לשינוי, ולכן גם הם מתרחבים, אפילו כשהאובייקט שמור ב-const. זו הדרך הנפוצה ביותר להיתקל ב-literal types בלי כוונה:

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

index.ts(7,15): error TS2345: Argument of type 'string' is not assignable to parameter of type '"GET" | "POST"'.

הטיפוס שמוסק ל-req הוא { url: string; method: string }, כי קוד בהמשך יכול להריץ req.method = "DELETE". יש שלושה תיקונים:

אפשרות רביעית היא satisfies, שבודק את האובייקט מול טיפוס ובאותו זמן שומר על ה-literal types של המאפיינים שלו.

as const

as const הוא const assertion. שימו אותו אחרי ביטוי, והקומפיילר יסיק את הטיפוס הצר ביותר שהוא יכול:

  • ערכי string, number ו-boolean שומרים על ה-literal types שלהם
  • מאפייני אובייקט הופכים ל-readonly
  • array literals הופכים ל-tuples של readonly באורך קבוע

ה-assertion קיים רק בזמן קומפילציה. ה-JavaScript שנוצר הוא אותו object literal בלי as const, ולכן שום דבר לא מונע מקוד אחר לשנות אותו בזמן ריצה. אם אתם צריכים הבטחה בזמן ריצה, קראו גם ל-Object.freeze.

טיפוס Union ממערך as const

דפוס נפוץ הוא לשמור את הערכים המותרים במערך אחד, שאפשר לעבור עליו בלולאה בזמן ריצה, ולגזור ממנו את טיפוס ה-union. (typeof arr)[number] פירושו "הטיפוס של כל איבר ב-arr".

בלי as const, ROLES היה string[] ו-Role היה string רגיל. ה-cast ל-readonly string[] ב-isRole נחוץ כי includes על tuple של literals מקבל רק את ה-literals האלה, והמטרה של הפונקציה היא לבדוק מחרוזת שאולי אינה אחד מהם. אותו דפוס עם אובייקט עובד גם למפות של מפתח וערך: const Status = { Active: "active", Banned: "banned" } as const, ואז type Status = (typeof Status)[keyof typeof Status].

const Type Parameters

פונקציה גנרית בדרך כלל מרחיבה את ה-literals שמעבירים לה. מאז TypeScript 5.0 אפשר לסמן type parameter כ-const, וכך הקומפיילר מסיק את הארגומנט כאילו היה עליו as const, בלי לבקש מהקורא לכתוב את זה.

זה בעיקר כלי לכותבי ספריות: הגדרות routes, builders ו-helpers של סכמות משתמשים בו כדי שהקוראים יקבלו טיפוסים מדויקים מ-literals רגילים.

המשמעויות של const

מילת המפתח const מופיעה בארבעה מקומות שונים בקוד TypeScript:

תחבירסוגמה הוא עושה
const x = 1הצהרה של JavaScriptאי אפשר להשים ערך חדש למשתנה; ערך literal שומר על ה-literal type שלו
expr as constassertion של TypeScriptהטיפוס הצר ביותר: literals, מאפייני readonly, tuples של readonly
function f<const T>()type parameter של TypeScriptמסיק ארגומנטים כאילו היה עליהם as const
const enum E {}enum של TypeScriptenum שהאיברים שלו מוטמעים בקוד בזמן קומפילציה

אף אחד מהם לא מקפיא אובייקט בזמן ריצה. const obj = { a: 1 } עדיין מאפשר obj.a = 2; רק השמה מחדש של obj עצמו היא שגיאה.

שאלות נפוצות

מה זה literal type ב-TypeScript?

טיפוס שמאפשר ערך אחד בדיוק. "GET" הוא טיפוס שהערך היחיד שלו הוא המחרוזת "GET", 404 הוא טיפוס שהערך היחיד שלו הוא המספר 404, ו-true הוא טיפוס שהערך היחיד שלו הוא true. הם הכי שימושיים כשמשלבים אותם ל-unions, כמו type Method = "GET" | "POST".

מה as const עושה ב-TypeScript?

as const הוא const assertion. הוא אומר לקומפיילר להסיק את הטיפוס הצר ביותר לביטוי: ערכי מחרוזת ומספר שומרים על ה-literal types שלהם, מאפייני אובייקט הופכים ל-readonly, ו-array literals הופכים ל-tuples של readonly. הוא משנה רק את הטיפוס; הערך בזמן ריצה הוא אותו אובייקט או מערך רגיל, והוא לא מוקפא.

למה TypeScript מסיק string במקום ה-literal שלי?

כי הערך ניתן לשינוי. אפשר להשים ערך חדש ל-let x = "a" ולמאפיין ב-{ method: "GET" } בהמשך, ולכן TypeScript מרחיב אותם ל-string. משתנה const שומר על ה-literal type "a". כדי לשמור על literals בתוך אובייקט, תנו לו annotation עם ה-literal type, השתמשו ב-as const, או השתמשו ב-satisfies.

מה ההבדל בין const ל-as const?

const הוא הצהרה של JavaScript: אי אפשר להשים ערך חדש למשתנה, אבל עדיין אפשר לשנות את האובייקט שהוא מצביע עליו. as const הוא type assertion של TypeScript: הוא הופך כל מאפיין של הערך ל-readonly ול-literal במערכת הטיפוסים. אף אחד מהם לא מקפיא את האובייקט בזמן ריצה; לשם כך השתמשו ב-Object.freeze.

איך מקבלים טיפוס union ממערך של מחרוזות?

הצהירו על המערך עם as const, ואז גשו לטיפוס שלו עם number: const roles = ["admin", "user"] as const; type Role = (typeof roles)[number]; נותן "admin" | "user". בלי as const המערך הוא string[] והתוצאה היא פשוט string.

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

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

להתחיל