javascript - איך מורחבת מחלקה ב-Typescript שמוטענת בצורה דינמית


אני משתמש ב-Google Maps JS API יחד עם המרחב השמות google.maps דרך npm install @types/googlemaps. אני מאמין שה-API נטען בצורה דינמית ולכן הגלובלי JS של google.maps אינו זמין מיד.

אבל אני לא מבין למה אני מקבל את שגיאת הריצה: Uncaught ReferenceError: google is not defined כשאני מנסה להרחיב את google.maps.Marker עם class אבל לא עם interface

// No problem!
export interface UuidMarker extends google.maps.Marker {
  uuid: string;
}

// Uncaught ReferenceError: google is not defined!!
export class UuidMarker0 extends google.maps.Marker {
  uuid: string;
  constructor(uuid: string, options?: gmMarkerOptions) {
    super(options);
    this.uuid = uuid;
  }
}

גישה חלופית תוך שימוש בממשק בלבד

// this works
export function UuidMarkerFactory(uuid: string, marker: google.maps.Marker): google.maps.Marker & {uuid:string} {  
  return Object.assign(marker, {uuid});
}

// this fails with google is not defined!! 
export function UuidMarkerFactory0(uuid: string, options?: any): google.maps.Marker & {uuid:string} {
  if (typeof google == "undefined") return null
  return Object.assign(new google.maps.Marker(options), {uuid});
}

מהי השיטה הטובה ביותר להרחבת מחלקה שטעונה בצורה דינמית?

מידע נוסף

אני משתמש ב-ionic2@RC0 שמשתמש ב-rollup כדי לאגד את כל המודולים. כל הקוד שלי ב-TypeScript ו-node_modules נאסף לתוך סקריפט אחד main.js עם מפות מקור. ה-API של Google Maps נטען על ידי הסקריפט angular2-google-maps.

אם אני מרחיב באמצעות interface (שעובד יותר בצורה "ידידותית" לטייפסקריפט), איזה דפוס אני יכול להשתמש בו כדי ליצור אובייקט שמתאים לממשק UuidMarker?

תשובה #1

בתהליך הקומפילציה אינך מקבל שום שגיאות כי לקומפיילר יש גישה להגדרות של google.maps שהתקנת באמצעות @types.

עם זאת, בזמן ריצה הקובץ שלך כנראה נטען לפני שספריית google.maps נטענה ולכן המפרש לא מצליח למצוא את האובייקט google.maps.Marker.

אתה צריך לטעון את הקובץ שלך רק אחרי שאתה יודע שקובץ google.maps נטען בהצלחה.

אתה לא מקבל שגיאות בזמן ריצה עבור הממשק UuidMarker כי הוא לא קיים בזמן ריצה.
לממשקים אין קיום ב-JavaScript, הם משמשים רק את המהדר של TypeScript ולא "מתורגמים" ל-JS.

אתה יכול לעשות טריק על ידי הצבת הגדרת המחלקה בתוך פונקציה.
בצורה זו המפרש לא יבצע אותה לפני שהפונקציה הזו נקראת, מה שיכול להיות לאחר שטעינת ספריית גוגל מפות הושלמה:

interface UuidMarker extends google.maps.Marker {
    uuid: string;
}

let UuidMarker0: { new (uuid: string, options?: gmMarkerOptions): UuidMarker };

function classLoader() {
    UuidMarker0 = class UuidMarker0 extends google.maps.Marker implements UuidMarker {
        uuid: string;

        constructor(uuid: string, options?: gmMarkerOptions) {
            super(options);
            this.uuid = uuid;
        }
    }
}

(קוד במגרש המשחקים המדמה זאת)

גישה נוספת היא לבטל את המחלקה כפי שהצעת ופשוט לעשות:

function UuidMarker(uuid: string, marker: google.maps.Marker): google.maps.Marker & { uuid: string } {
    return Object.assign(marker, { uuid });
}

ערוך

תחביר זה:

type MyType = google.maps.Marker & { uuid: string };

נקרא סוגי הצטלבות וזה אומר ש-MyType יש את כל מה שיש ל-google.maps.Marker בנוסף לתכונה uuid.

דוגמה פשוטה:

interface Point {
    x: number;
    y: number;
}

type Point3D = Point & { z: number };

let p1: Point = { x: 0, y: 0 };
let p2: Point3D = { x: 0, y: 0, z: 0 };