ב-TypeScript, האם יש סיבה להעדיף const foo: Type={...} או const foo={ ... } as Type?
ב-TypeScript, יש שתי דרכים להצהיר שקבוע אמור להתאים לממשק:
interface Foo { x: string; }
const HELLO: Foo = { x: 'hello' };
או
interface Foo { x: string; }
const HELLO = { x: 'hello' } as Foo;
נראה שלדברים האלה יש את אותה השפעה: TypeScript מטפלת ב-HELLO כאילו יש לו סוג של Foo, והקצאת כל חבר שאינו מופיע ב-interface Foo גורמת לשגיאה.
האם יש סיבה להעדיף צורה אחת על פני השנייה? האם הן שוות בדיוק?
תשובה #1
בדוגמה שלך, ההקצאה מתבררת להיות זהה. עם זאת, הפונקציונליות של הקלדת משתנה כפי שבדוגמה הראשונה שלך והמרת טיפוס של המשתנה שלך, כמו בדוגמה השנייה, אינן זהות.
הנה דוגמה מורחבת:
interface Foo { x: string; }
interface Foo2 extends Foo { y: string; }
// typing the variables
const hello: Foo = { x: 'hello' }; // works
const hello2: Foo2 = { x: 'hello', y: 'hello' }; // works
const hello3: Foo2 = { x: 'hello' }; // does not work because property 'y' is missing
// typecasting the variables
const hello4 = hello as Foo2; // works
const hello5 = hello2 as Foo2; // works
const hello6 = { x: 'hello' } as Foo2; // works
// typing the variables
const hello7: Foo = {}; // does not work because property 'x' is missing
const hello8: Foo2 = {}; // does not work because property 'y' is missing
// typecasting the variables
const hello9 = <Foo>{}; // works
const hello10 = <Foo2>{}; //works
// typing must match exact form, where typecasting does not, as long as interface is honored
const hello11: Foo = { x: 'hello', y: 'hello' }; // fails because of extra property 'y'
const hello12 = { x: 'hello', y: 'hello' } as Foo; // works
כפי שאתה יכול לראות, הקלדת המשתנה עובדת רק אם האובייקט שהוקצה לו תואם בדיוק לצורה. המרת סוג של המשתנה עובדת גם אם האובייקט שהוקצה אינו תקין, מכיוון שזה בעצם אומר שאתה אומר ל-TypeScript שתדאג שהוא יהיה נכון בשלב מסוים.. למשל אם אתה מאתחל עם אובייקט לא תקין, ואז באמצעות לוגיקה כלשהי מגדיר את התכונות החסרות שנותרו.
ניתן לבצע המרת טיפוס על ידי שימוש במילת המפתח as או על ידי קידום המשתנה שאליו מוקצה <type>.
תשובה #2
זה עבור Typescript 2.1.4. אני לא מודע להבדלים בגרסאות אחרות.
ראשית, אתה צריך לקבל שגיאת הקומפילציה אם תעשה זאת:
interface Foo { x: string; }
const HELLO: Foo = { x:'hello', y: 'goodbye' }; // Will not compile.
במקרה השני, אתה בעצם מפשט אותו לממשק Foo, אז מבחינת הקומפילציה, זה חוקי לחלוטין.
interface Foo { x: string; }
const HELLO = { x:'hello', y: 'goodbye' } as Foo; // No compilation error.
const yValue = HELLO.y; // compilation error because "y" should not exist according to the interface
const yValue2 = (HELLO as any).y // suddenly works; yValue2 === "goodbye"
הקודם לא יאפשר לכם ליצור את האובייקט עם נתונים נוספים כך שהוא עשוי למנוע טעויות טיפשיות. האחרון יאפשר לכם לבצע המרה של אובייקט שכבר נוצר שיש לו את אותן הערכים כמו הממשק. לאחר מכן תוכלו להעביר את האובייקט הזה לפונקציות או מחלקות אחרות כאילו שהוא הממשק. זה בעצם יסתיר את הערכים הנוספים.
