TypeScript 2.0 - מטרת מילת המפתח `declare` ב-TypeScript
מה משמעות המילת המפתח declare?
type Callback = (err: Error | String, data: Array<CalledBackData>) => void;
נגד
declare type Callback = (err: Error | String, data:Array<CalledBackData>) => void;
לא מצליחים למצוא מסמכים שמסבירים את מטרת מילת המפתח declare ב-TS. מה זה אומר?
תשובה #1
סיכום קצר
declare משמש כדי לומר למתרגם "הדבר הזה (בדרך כלל משתנה) כבר קיים, ולכן ניתן להתייחס אליו על ידי קוד אחר, וגם אין צורך לקמפל את ההצהרה הזו ל-JavaScript כלשהו".
שימוש נפוץ:
אתה מוסיף הפניה לדף האינטרנט שלך לקובץ JavaScript שהקומפיילר אינו יודע עליו כלום. אולי מדובר בסקריפט שמגיע מדומיין אחר כמו 'foo.com'. כאשר הוא מוערך, הסקריפט יוצר אובייקט עם כמה שיטות API שימושיות ומקצה אותו למזהה 'fooSdk' בטווח הגלובלי.
אתה רוצה שהקוד שלך ב-TypeScript יוכל לקרוא ל-fooSdk.doSomething(), אבל מכיוון שהקומפיילר שלך לא יודע שהמשתנה fooSdk קיים, תקבל שגיאת קומפילציה.
לאחר מכן אתה משתמש במילת המפתח declare כאמצעי לומר למתרגם "תסמוך עליי, משתנה זה קיים ויש לו את הטיפוס הזה". המתרגם ישתמש בהצהרה זו כדי לבדוק קוד אחר באופן סטטי אך לא יתעתק את זה לכל JavaScript ביצוא.
declare const fooSdk = { doSomething: () => boolean }
גרסאות חדשות יותר של Typescript דורשות תחביר מעט שונה:
declare const fooSdk : { doSomething: () => boolean }
באותה מידה, ניתן להוסיף את מילת המפתח declare לתכונות מחלקה כדי להודיע לקומפיילר לא להפיק קוד שייצור את התכונה הזו, בהנחה שיש לכם קוד משלכם שיעניק לה את המימוש שהקומפיילר אינו יודע עליו או אינו מבין.
הדוגמה הספציפית שלך שונה מכיוון שאתה מכריז על טיפוס, לא על משתנה, טיפוסים כבר לא מתורגמים ל-JavaScript בכלל. אני לא יודע אם יש סיבה להכריז על טיפוס.
תשובה #2
הנה דוגמה מהעולם האמיתי.
יש לי אפליקציית TypeScript React שמשתמשת ב-Webpack Hot Middleware. ה-Webpack Hot Middleware לא נכתב ב-TypeScript, אלא ב-JavaScript ישן וטוב. לכן אין לו הצהרות טיפוסים שהקומפיילר של TypeScript יכול לבדוק מולן.
כשאני מריץ את הקוד שלי, האובייקט module מה-Webpack Hot Middleware קיים, ואני יכול להדפיס אותו בקונסול למרות שהוא JavaScript ישן וטוב שמתחבא באפליקציית React TypeScript החדשה והמפוארת שלי.
האובייקט module גם מכיל מפתחות, כגון module.hot, ולמפתחות יכולים להיות ערכים. אך קומפיילר זמן העיצוב של TypeScript (לפי ניירת ב-VSCode לפחות) מקשקש בקו אדום מתחתיו ואומר property 'hot' does not exist. אך הוא כן קיים!
כדי לגרום למתרגם TypeScript להסכים, הכריז עליו כך:
declare let module: any
האובייקט הקיים module, עכשיו יש לו סוג any, מה שמריץ את הקומפלייר של TypeScript לשביעות רצון עכשיו, הקו האדום נעלם ועכשיו אני יכול להמשיך לקמפל ולכתוב את הקוד האחר שלי.
אם תסיר את מילת המפתח declare, ותכתוב פשוט let module: any, זה לא יקמפל, במקום זאת יאמר ש‑'module' כבר קיים. זה מה שהמונח "ambient" בתשובה המקובלת מתכוון בו.
תשובה #3
ממסמכי Typescript:
TypeScript - עבודה עם ספריות JavaScript אחרות
כדי לתאר את הצורה של ספריות שלא נכתבו ב-TypeScript, אנחנו צריכים להצהיר על ה-API שהספריה חושפת. מכיוון שרוב ספריות ה-JavaScript חושפות רק כמה אובייקטים ברמה העליונה, מרחבי שמות הם דרך טובה לייצגם.
אנו קוראים להצהרות שאינן מגדירות יישום "ambient". בדרך כלל הן מוגדרות בקבצי .d.ts. אם אתם מכירים את C/C++, תוכלו לחשוב על אלו כקבצי .h. בואו נסתכל על כמה דוגמאות.
מרחבי שמות סביבתיים
הספרייה הפופולרית D3 מגדירה את הפונקציונליות שלה באובייקט גלובלי הנקרא d3. בגלל שהספרייה הזו נטענת דרך תגית (במקום דרך טוען מודולים), ההכרזה שלה משתמשת במרחבי שמות כדי להגדיר את הצורה שלה. כדי שמתמלל TypeScript יראה את הצורה הזו, אנו משתמשים בהכרזת מרחב שמות אמביינטי. לדוגמה, נוכל להתחיל לכתוב אותה באופן הבא:
D3.d.ts (קטע מפושט) :
declare namespace D3 { export interface Selectors { select: { (selector: string): Selection; (element: EventTarget): Selection; }; } // (...) }
תשובה #4
אתה יכול להשתמש ב-declare כדי להודיע למתרגם על סוגים לפני שאתה כותב כל קוד מימוש ו-TypeScript יהיה שמח.
declare function foo(name: string): string
תשובה #5
שקול את התרחיש הבא: יש לך פרויקט Typescript. אז אתה מחליט לכתוב מודול ב-JavaScript מסיבה כלשהי ולהשתמש בו בקוד ה-Typescript שלך.
// getMyName.js
module.exports = function getMyName(name) {
return name;
}
אז אתה מייבא את זה לתוך קוד TypeScript שלך
// myCode.ts
import getMyName from 'getMyName'; //getMyName is a JS module not typescript
כאשר מייבאים את מודול ה-JS הזה (allowJs באפשרות הקומפיילר צריך להיות מוגדר ל-false אחרת, קבצי JS ייושמו ללא שום הגדרות סוגים), הקומפיילר של Typescript מתלונן על כך שהמודול הזה אינו מודול בכלל!
Could not find a declaration file for module '/absolute/path/to/getMyName.ts'זה בגלל ש-TypeScript לא מתרגם לקוד JS בעצמו. כדי לעקוף בעיה זו עלינו לספק הגדרת סוג (filename.d.ts) עבור המודול JS שלנו. לשם כך ניתן להשתמש ב-declare ו-export.
// getMyName.d.ts
declare function getMyName(name: string): string;
export default getMyName;
לאחר מכן, כאשר אנחנו מייבאים את מודול ה-JS שלנו, TypeScript לא מתלונן על שום דבר כי עכשיו יש לו גישה לסוגים.
