Home > database >  Typescript ReferenceError: google is not defined, but only in standalone files
Typescript ReferenceError: google is not defined, but only in standalone files

Time:12-05

I'm building a React app with an embedded Google Map.

I've got a custom menu element that I want to display on the map after a click. Google's docs instruct me to 'implement' (although I think in Typescript terms, they mean extend) the google.maps.OverlayView class in order to render elements over the map.

When I define the class ContextMenu extends google.maps.OverlayView class inline, the code compiles fine and my element shows up on click. I want to define this class in a separate file, using Typescript.

However, when I move ContextMenu to a separate file, React errors out with ReferenceError: google is not defined.

Any idea how to 'import the namespace' such that ContextMenu.ts knows where google is? It seems like I am missing something fundamental about Typescript here, but none of their documentation I've been able to find has discussed the practice of creating classes with external namespaces.

Or is extends the wrong way to do this here? Should I just follow Google's instructions, even in Typescript which exists to avoid messing with prototypes?

Inherit from this class by setting your overlay's prototype: MyOverlay.prototype = new google.maps.OverlayView();.


Working code:

// App.tsx
import React from 'react'
import { Wrapper } from '@googlemaps/react-wrapper'
// cannot define ContextMenu here

const App: React.VFC = () => {
  // cannot define ContextMenu here
  const onClick = (e: google.maps.MapMouseEvent) => {
    // CAN define ContextMenu here
    class ContextMenu extends google.maps.OverlayView {
      private origin_: google.maps.LatLng
      constructor(origin: google.maps.LatLng) {
        super()

        this.origin_ = origin
      }
    }

    const menu =  new ContextMenu(e.latLng)
  }

  return (
    <Wrapper ...>
    // Map goes here
    </Wrapper>
  )
}


Broken code:

// App.tsx as above, without ContextMenu defined.

// ContextMenu.ts
class ContextMenu extends google.maps.OverlayView {
  // ...
}

CodePudding user response:

It is not possible to directly extend a google.maps.* class since it actually isn't available (this might depend on tsconfig target, but I haven't tested). You can use the following pattern in TypeScript to delay.

export interface OverlayViewSafe extends google.maps.OverlayView {}

/**
 * Extends an object's prototype by another's.
 *
 * @param type1 The Type to be extended.
 * @param type2 The Type to extend with.
 * @ignore
 */
// eslint-disable-next-line @typescript-eslint/no-explicit-any
function extend(type1: any, type2: any): void {
  // eslint-disable-next-line prefer-const
  for (let property in type2.prototype) {
    type1.prototype[property] = type2.prototype[property];
  }
}

/**
 * @ignore
 */
export class OverlayViewSafe {
  constructor() {
    // We use the extend function for google.maps.OverlayView
    // because it might not always be available when the code is defined.
    extend(OverlayViewSafe, google.maps.OverlayView);
  }
}
  • Related