javascript - ¿Cómo se extiende una clase en Typescript que se carga dinámicamente?
Estoy usando la API de Google Maps JS junto con el espacio de nombres google.maps a través de npm install @types/googlemaps. Creo que la API se carga dinámicamente, por lo que el global JS google.maps no está disponible de inmediato.
Pero no entiendo por qué obtengo el error de tiempo de ejecución: Uncaught ReferenceError: google is not defined cuando intento extender google.maps.Marker con una class pero no con una 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;
}
}
Enfoque alternativo usando solo la interfaz
// 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});
}
¿Cuál es la mejor práctica para extender una clase que se cargó dinámicamente?
Información Adicional
Estoy usando ionic2@RC0 que utiliza rollup para empaquetar todos los módulos. Todo mi TypeScript y node_modules se empaquetan en un solo script main.js con mapas de origen. La API real de Google Maps se carga mediante el script angular2-google-maps.
Si extiendo usando una interface (que parece más "amigable" con TypeScript), ¿qué patrón puedo usar para crear un objeto que se ajuste a la interfaz UuidMarker?
Respuesta #1
En el proceso de compilación no estás obteniendo ningún error porque el compilador tiene acceso a las definiciones de google.maps que instalaste usando @types.
Sin embargo, en tiempo de ejecución probablemente tu archivo se está cargando antes de que la biblioteca google.maps haya cargado y por lo tanto el intérprete no puede encontrar el objeto google.maps.Marker.
Necesitas cargar tu archivo solo después de saber que el archivo google.maps se ha cargado correctamente.
No obtienes errores en tiempo de ejecución por la interfaz UuidMarker porque no existe en tiempo de ejecución.
Las interfaces no existen en JavaScript, solo son utilizadas por el compilador de TypeScript y no se están "traduciendo" a JS.
Puedes hacer un truco colocando la definición de la clase dentro de una función.
De esta manera, el intérprete no la ejecutará antes de que se llame a esta función, lo que puede ser después de que se haya cargado la librería de Google Maps:
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;
}
}
}
(código de playground que simula esto)
Otro enfoque es eliminar la clase como sugeriste y simplemente hacer:
function UuidMarker(uuid: string, marker: google.maps.Marker): google.maps.Marker & { uuid: string } {
return Object.assign(marker, { uuid });
}
Editar
Esta sintaxis:
type MyType = google.maps.Marker & { uuid: string };
Se llama Tipos de Intersección y significa que MyType tiene todo lo que google.maps.Marker tiene más la propiedad uuid.
Un ejemplo simple:
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 };
