Connecting the API with TypeScript
Usual connection
<!DOCTYPE html>
<html>
<head>
<!-- Substitute the value of the real key instead of YOUR_API_KEY -->
<script src="https://js.api.mappable.world/v3/?apikey=YOUR_API_KEY&lang=en_US"></script>
<script type="module" src="index.js"></script>
</head>
<body>
<div id="app" style="width: 600px; height: 400px"></div>
</body>
</html>
import type { MMapLocationRequest } from 'mappable';
async function initMap(): Promise<void> {
await mappable.ready;
const LOCATION: MMapLocationRequest = {
center: [25.229762, 55.289311],
zoom: 9
};
const { MMap, MMapDefaultSchemeLayer } = mappable;
const map = new MMap(document.getElementById('app'), { location: LOCATION });
map.addChild(new MMapDefaultSchemeLayer({}));
}
initMap();
{
"compilerOptions": {
"target": "es2015",
"lib": [
"dom",
"dom.iterable",
"esnext"
],
"esModuleInterop": true,
"moduleResolution": "node",
"typeRoots": [
"./node_modules/@types",
"./node_modules/@mappable-world/mappable-types"
],
"paths": {
"mappable": [
"./node_modules/@mappable-world/mappable-types"
]
}
}
}
{
"devDependencies": {
"@mappable-world/mappable-types": "^0.0.10",
"http-server": "14.1.1",
"typescript": "5.2.2"
},
"scripts": {
"compile": "./node_modules/.bin/tsc",
"start": "npx http-server ."
}
}
Install the dependencies, compile typescript
and start the local server:
npm install
npm run compile
npm run start
Open application
Specificities of the usual connection
-
In
package.json
adding a dev-dependency on the package@mappable-world/mappable-types
.It is recommended to install the latest version:
npm i --save-dev @mappable-world/mappable-types@latest
-
In
tsconfig.json
we setCompilerOptions.typeRoots
with a list of paths to file types. Adding the path to the package@mappable-world/mappable-types
there, so that the namespacemappable
with types appears in the global scope.Note
The namespace
mappable
contains all the class types that the JS API provides, but they are not available in the runtime until the resolvingmappable.ready
. -
In
tsconfig.json
we setCompilerOptions.paths
, in which we inform the ts compiler that when importing the packagemappable
, its content should be searched for in the specified path. Thanks to this, you can import types in project files as if they are not in the@mappable-world/mappable-types
, but in themappable
package:import type { MMapLocationRequest } from 'mappable';
All types must be imported from the root.
The internal structure is not guaranteed and may change over time.
-
In the
script
tag that loads the compiled project js-file, specify thetype="module"
attribute so that the browser activates support forESM
in js files.If this is not done in the example, an error will occur:
SyntaxError: Unexpected token 'export {}'
Connecting via top-level-await (recommended)
<!DOCTYPE html>
<html>
<head>
<!-- Substitute the value of the real key instead of YOUR_API_KEY -->
<script src="https://js.api.mappable.world/v3/?apikey=YOUR_API_KEY&lang=en_US"></script>
<script type="module" src="index.js"></script>
</head>
<body>
<div id="app" style="width: 600px; height: 400px"></div>
</body>
</html>
import type { MMapLocationRequest } from 'mappable'
import { MMap, MMapDefaultSchemeLayer } from './lib/mappable.js'
const LOCATION: MMapLocationRequest = {
center: [25.229762, 55.289311],
zoom: 9
};
const map = new MMap(
document.getElementById('app'),
{
location: LOCATION
}
);
map.addChild(new MMapDefaultSchemeLayer());
await mappable.ready;
export const {MMap, MMapDefaultSchemeLayer} = mappable;
{
"compilerOptions": {
"target": "es2017",
"lib": [
"dom",
"dom.iterable",
"esnext"
],
"esModuleInterop": true,
"module": "esnext",
"moduleResolution": "node",
"typeRoots": [
"./node_modules/@types",
"./node_modules/@mappable-world/mappable-types"
],
"paths": {
"mappable": [
"./node_modules/@mappable-world/mappable-types"
]
}
}
}
{
"devDependencies": {
"@mappable-world/mappable-types": "^0.0.10",
"http-server": "14.1.1",
"typescript": "5.2.2"
},
"scripts": {
"compile": "./node_modules/.bin/tsc",
"start": "npx http-server ."
}
}
Install the dependencies, compile typescript
and start the local server:
npm install
npm run compile
npm run start
Open application
Specificities
-
In the script tag that loads the compiled project js, specify the attribute
type="module"
to activate support for ECMAScript Modules (ESM) and top-level-await:<script type="module" src="index.js"></script>
ESM support when using bundles
If a bundler is used to build the project, for example Webpack, then in the
package.json
file needs to add"type": "module"
-
In
tsconfig.json
, for top-level-await to work correctly, the parametercompilerOptions.module
must be set to one of the following values:es2022
,esnext
,system
orpreserve
. Also thecompilerOptions.target
parameter must bees2017
or higher. -
In the file
lib/mappable.js
we are waiting for the JS API to be fully loaded, after which we export the necessary map components for use in other parts of the project:await mappable.ready; export const {MMap, MMapDefaultSchemeLayer} = mappable;
-
Using top-level-await in
lib/mappable.js
guarantees the execution ofmappable.ready
before importing map components, so the project code can be written shorter and neater (the asynchronous initMap function is no longer needed):import { MMap, MMapDefaultSchemeLayer } from './lib/mappable.js' const map = new MMap({...});
Connecting with Webpack
Method 1
<!DOCTYPE html>
<html>
<head>
<!-- Substitute the value of the real key instead of YOUR_API_KEY -->
<script src="https://js.api.mappable.world/v3/?apikey=YOUR_API_KEY&lang=en_US"></script>
<script src="build/bundle.js"></script>
</head>
<body>
<div id="app" style="width: 600px; height: 400px"></div>
</body>
</html>
import { type MMapLocationRequest } from 'mappable';
async function initMap() {
await mappable.ready;
const LOCATION: MMapLocationRequest = {
center: [25.229762, 55.289311],
zoom: 9
};
const { MMap, MMapDefaultSchemeLayer } = mappable;
const map = new MMap(document.getElementById('app'), {location: LOCATION});
map.addChild(new MMapDefaultSchemeLayer({}));
}
initMap();
{
"compilerOptions": {
"target": "es2015",
"lib": [
"dom",
"dom.iterable",
"esnext"
],
"esModuleInterop": true,
"moduleResolution": "node",
"typeRoots": [
"./node_modules/@types",
"./node_modules/@mappable-world/mappable-types"
],
"paths": {
"mappable": [
"./node_modules/@mappable-world/mappable-types"
]
}
},
}
const path = require('path');
module.exports = {
mode: 'development',
entry: './index.js',
output: {
filename: 'bundle.js',
path: path.resolve(__dirname, 'build'),
},
devtool: 'cheap-source-map'
};
{
"scripts": {
"compile": "./node_modules/.bin/tsc",
"build": "webpack",
"start": "npx http-server ."
},
"devDependencies": {
"@mappable-world/mappable-types": "^0.0.10",
"http-server": "14.1.1",
"typescript": "5.2.2",
"webpack": "5.88.2",
"webpack-cli": "5.1.4"
}
}
Put the dependencies, compile typescript
, build the project and run the local server:
npm install
npm run compile
npm run build
npm run start
Open application
This method is no different from the previous connection. Only the build step from the project js
files is added to a single build/bundle.js
Method 2
<!DOCTYPE html>
<html>
<head>
<script src="build/bundle.js"></script>
</head>
<body>
<div id="app" style="width: 600px; height: 400px"></div>
</body>
</html>
import type { MMapLocationRequest } from '@mappable-world/mappable-types';
import {MMap, MMapDefaultSchemeLayer} from '@mappable-world/mappable-types';
const LOCATION: MMapLocationRequest = {
center: [25.229762, 55.289311],
zoom: 9
};
const map = new MMap(document.getElementById('app'), {location: LOCATION});
map.addChild(new MMapDefaultSchemeLayer({}));
{
"compilerOptions": {
"target": "es2015",
"lib": [
"dom",
"dom.iterable",
"esnext"
],
"esModuleInterop": true,
"moduleResolution": "node",
"typeRoots": [
"./node_modules/@types",
"./node_modules/@mappable-world/mappable-types"
]
},
}
const path = require('path');
module.exports = {
mode: 'development',
entry: './index.js',
output: {
filename: 'bundle.js',
path: path.resolve(__dirname, 'build'),
},
externals: {
'@mappable-world/mappable-types': [
`promise new Promise((resolve) => {
if (typeof mappable !== 'undefined') {
return mappable.ready.then(() => resolve(mappable));
}
const script = document.createElement('script');
script.src = "https://js.api.mappable.world/v3/?apikey=YOUR_API_KEY&lang=en_US";
script.onload = () => {
mappable.ready.then(() => resolve(mappable));
};
document.head.appendChild(script);
})`
]
},
devtool: 'cheap-source-map'
};
{
"scripts": {
"compile": "./node_modules/.bin/tsc",
"build": "webpack",
"start": "npx http-server ."
},
"devDependencies": {
"@mappable-world/mappable-types": "^0.0.10",
"http-server": "14.1.1",
"typescript": "5.2.2",
"webpack": "5.88.2",
"webpack-cli": "5.1.4"
}
}
Put the dependencies, compile typescript
, build the project and run the local server:
npm install
npm run compile
npm run build
npm run start
Open application
Specificities
-
It is no longer necessary to connect the tag
<script>
in the header of theHTML
page. This is done by webpack itself. -
In
webpack.config.js
we declare an external variable@mappable-world/mappable-types
, in which we specify the path to the API loader and the promisemappable.ready
is resolved there.This makes it possible to import JS API types and classes in the project code as if the code is delivered not through a global variable, but through the
npm
package@mappable-world/mappable-types
:// Importing Types import type { MMapLocationRequest } from '@mappable-world/mappable-types'; // Importing the runtime code import {MMap, MMapDefaultSchemeLayer} from '@mappable-world/mappable-types';
-
In
tsconfig.json
is no longer needed thecompilerOptions.paths.mappable
setting. -
With this connection, fully loaded JS API modules become available in the assembled project
js
file and the execution ofmappable.ready
is guaranteed, so the project code can be written shorter and neater (the asynchronousinitMap
function is no longer needed):import type { MMapLocationRequest } from '@mappable-world/mappable-types'; import {MMap, MMapDefaultSchemeLayer} from '@mappable-world/mappable-types'; const {MMap} = mappable; const map = new MMap({...});
Alias
Note that in the last example, the import comes from a package named @mappable-world/mappable-types
. This name can be changed using the alias in package.json
:
{
"devDependencies": {
// ...
"@mappable-world/mappable": "npm:@mappable-world/mappable-types@0.0.10",
// ...
}
}
Don't forget to update the name of the external variable in webpack.config.js
:
module.exports = {
//...
externals: {
'@mappable-world/mappable': [
// ...
]
}
}
After all the changes, you can import types and classes from the package @mappable-world/mappable
:
import type { MMapLocationRequest } from '@mappable-world/mappable';
import {MMap, MMapDefaultSchemeLayer} from '@mappable-world/mappable';