Template literal type בונה טיפוסי string literal באותו תחביר של גרשיים הפוכים כמו template string ב-JavaScript. `on${Capitalize<"click" | "focus">}` הוא הטיפוס "onClick" | "onFocus", שהקומפיילר מחשב:
Template literal types קיימים רק בזמן קומפילציה. הם בודקים string literals וערכים עם טיפוס בזמן כתיבת הקוד, ולא מוסיפים דבר לפלט ה-JavaScript. טבלת ה-handlers שלמעלה משתמשת ב-Record כדי לחייב פונקציה אחת לכל שם.
תחביר
בתוך הגרשיים ההפוכים כותבים טקסט ליטרלי ומצייני מקום ${...}. מציין מקום מחזיק טיפוס, לא ערך: טיפוס ליטרלי של string, number, bigint או boolean, union שלהם, או אחד הטיפוסים הרחבים string, number, bigint, boolean, null ו-undefined.
מציין מקום עם טיפוס רחב כמו string או number יוצר תבנית: הטיפוס נשאר `hello ${string}` וכל מחרוזת שמתאימה לו מתקבלת. מציין מקום עם union סופי, כמו boolean, מורחב לאיברים שלו.
Unions מוכפלים
עם כמה unions, התוצאה היא כל הצירופים:
שלושה גדלים כפול שני גוונים נותנים שישה איברים. המספר גדל מהר: חמישה מצייני מקום שכל אחד מהם מחזיק union של עשר אותיות יתנו 100,000 איברים, ו-TypeScript מסרב עם error TS2590: Expression produces a union type that is too complex to represent. השתמשו במציין מקום רחב כמו ${string} כשלא צריך כל ערך מדויק.
Uppercase, Lowercase, Capitalize, Uncapitalize
ארבעה טיפוסים מובנים משנים את האותיות הגדולות והקטנות בטיפוסי string literal. הם intrinsic: ממומשים בתוך הקומפיילר, ולא כתובים ב-TypeScript.
| טיפוס | קלט | תוצאה |
|---|---|---|
Uppercase<S> | "hello world" | "HELLO WORLD" |
Lowercase<S> | "Content-Type" | "content-type" |
Capitalize<S> | "hello world" | "Hello world" |
Uncapitalize<S> | "UserName" | "userName" |
הם משנים רק טיפוסים. כדי לבנות את המחרוזת המתאימה בזמן ריצה, עדיין קוראים ל-toUpperCase() או חותכים ומגדילים את האות הראשונה בעצמכם, ואומרים ל-TypeScript שלתוצאה יש את הטיפוס המדויק:
ה-as נחוץ כי toUpperCase() מוגדרת כמחזירה string רגיל. החתימה של הפונקציה היא מה שהקוראים לה רואים, ולכן ל-capitalize("report") יש את הטיפוס הליטרלי "Report".
תבניות מחרוזת: ${number}px ודומיו
טיפוס תבנית מקבל כל מחרוזת בצורה מסוימת. הוא שימושי לערכי CSS, למזהים ולמפתחות עם קידומת ידועה:
${number} מקבל כל מחרוזת ש-JavaScript קורא כמספר, וזה מתירני יותר ממה שנראה: "-3px", "1e3px" ו-"0x10px" כולם עוברים את בדיקת הטיפוסים. התייחסו לתבניות האלה כהגנה מפני שגיאות הקלדה בליטרלים, לא כבדיקת תקינות מלאה.
Template literals עם mapped types
Template literal types שימושיים במיוחד כפסוקית as של mapped type, שם הם יוצרים שמות מאפיינים משמות מאפיינים אחרים:
string & K משאיר רק מפתחות מסוג string, כי Capitalize לא מקבל מספרים או symbols. כל callback מקבל את טיפוס הפרמטר שלו מהמאפיין שהוא עוקב אחריו.
פירוק מחרוזות עם infer
ב-conditional type, template literal יכול להתאים למחרוזת ולתפוס חלקים ממנה עם infer. כך מחלצים את שמות הפרמטרים מתבנית של נתיב:
אם משמיטים את postId בקריאה, הקומפיילר מדווח שהוא חסר.
ביטויי תבנית מורחבים ל-string
ביטוי template string בקוד רגיל מקבל את הטיפוס string, גם כשכל החלקים שלו הם טיפוסים ליטרליים. הוסיפו as const כדי לשמור את ה-template literal type:
בלי as const, השמה של loose ל-`log:${Level}` נכשלת, כי string יכול להיות כל דבר.
שאלות נפוצות
מה הם template literal types ב-TypeScript?
טיפוסי string literal שנכתבים עם גרשיים הפוכים ומצייני מקום ${...}, כמו template strings ב-JavaScript אבל ברמת הטיפוסים. type Greeting = `hello ${string}` מקבל כל מחרוזת שמתחילה ב-hello , ו-`on${Capitalize<"click">}` הוא הטיפוס הליטרלי "onClick".
מה קורה כששמים union בתוך template literal type?
התבנית מורחבת לכל אחד מהאיברים, ועם כמה unions מקבלים את כל הצירופים. `${"sm" | "lg"}-${"red" | "blue"}` הוא "sm-red" | "sm-blue" | "lg-red" | "lg-blue". צירופים גדולים מאוד נכשלים עם השגיאה TS2590.
מה עושים Uppercase, Lowercase, Capitalize ו-Uncapitalize?
אלה טיפוסים מובנים שממירים טיפוסי string literal: Uppercase<"id"> הוא "ID", Lowercase<"ID"> הוא "id", Capitalize<"name"> הוא "Name" ו-Uncapitalize<"Name"> הוא "name". הם משנים רק טיפוסים; כדי לשנות מחרוזת בזמן ריצה עדיין קוראים ל-toUpperCase() ולמתודות דומות.
האם template literal types בודקים מחרוזות בזמן ריצה?
לא. כמו כל טיפוס של TypeScript הם נמחקים, ולכן הם בודקים רק string literals וערכים עם טיפוס בזמן קומפילציה. מחרוזת שמגיעה בזמן ריצה, מ-JSON או מקלט של משתמש, היא פשוט string עד שבודקים אותה בקוד שלכם.
למה ה-template string שלי מקבל את הטיפוס string ולא טיפוס ליטרלי?
ביטוי תבנית כמו `on${event}` מורחב ל-string כשמשימים אותו למשתנה. הוסיפו as const (`on${event}` as const) או הצהירו על טיפוס היעד, ו-TypeScript ישמור את ה-template literal type, למשל "onclick" | "onfocus".