Using ES Modules in OpenSphere¶
Modules have a number of key differences from files using goog.provide
that are important for developers to keep in mind.
Module Scope¶
Every module has its own local scope. This prevents polluting the global window
context because all variables defined in the module are local by default.
Consider the following code at the root level of a JavaScript file:
const myString = 'Hello, World!';
If this were loaded in a normal script, window.myString
would be set to the string Hello, World!
. When loaded in a module, the variable is only accessible locally in the module. window.myString
will still be undefined
.
Module Exports¶
Note
This documentation is a basic export example. For more complete documentation, see the MDN import and export documentation.
To provide functions, classes, etc to external files, a module may use the export
keyword to define what to expose from its local scope. To make the above string available externally, a module could do the following:
goog.declareModuleId('my.module');
export const myString = 'Hello, World!';
exports = {myString};
Then to reference that string in another module:
import {myString} from './path/to/my/module.js';
console.log(myString);
Requiring an ES Module in Legacy Code¶
In a legacy goog.provide
file, goog.require
always returns null
and should never be assigned to a variable. Doing so would pollute the global context by adding the variable name to window
. To reference an ES module from a goog.provide
type file, you must use goog.module.get
in a restricted scope and assign the exports.
For example:
goog.provide('my.legacy');
goog.require('my.module');
my.legacy.printTheString = function() {
const {myString} = goog.module.get('my.module');
console.log(myString);
};
In a goog.module
file, goog.require
returns the exports so this can be simplified a bit.
goog.module('my.legacy');
const {myString} = goog.require('my.module');
const printTheString = function() {
console.log(myString);
};
exports = {printTheString};
Type Only Imports¶
If an import
or goog.require
is only needed to access types in a module, use goog.requireType
. This will only be used by the compiler for type checking and does not create a hard dependency on the required module. These calls will also be discarded from the compiled output.
// SomeEvent is a dependency and programmatically used in the file.
import SomeEvent from './path/to/someevent.js';
// The SomeEvent type is referenced in JSDoc, and is not a dependency.
const {default: SomeEvent} = goog.requireType('os.SomeEvent');
Note
When using goog.requireType
with an ES module, Closure will assign the default export to a default
property on the exports, and any named exports to like-named properties. This is why the above example reassigns the default
property to a more friendly name. This is also necessary when using goog.module.get
with an ES module.
Typedefs¶
@typedef
declarations are only used by the compiler, but must be exported if they’re used outside the file that declares them. Alternatively they can be moved to an extern to avoid the need for goog.requireType
to use them.
/**
* @typedef {{
* prop1: string,
* prop2: number
* }}
*/
export let MyType;