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.jsonadding 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.jsonwe setCompilerOptions.typeRootswith a list of paths to file types. Adding the path to the package@mappable-world/mappable-typesthere, so that the namespacemappablewith types appears in the global scope.Note
The namespace
mappablecontains all the class types that the JS API provides, but they are not available in the runtime until the resolvingmappable.ready. -
In
tsconfig.jsonwe 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 themappablepackage: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
scripttag that loads the compiled project js-file, specify thetype="module"attribute so that the browser activates support forESMin 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.jsonfile needs to add"type": "module" -
In
tsconfig.json, for top-level-await to work correctly, the parametercompilerOptions.modulemust be set to one of the following values:es2022,esnext,systemorpreserve. Also thecompilerOptions.targetparameter must bees2017or higher. -
In the file
lib/mappable.jswe 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.jsguarantees the execution ofmappable.readybefore 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 theHTMLpage. This is done by webpack itself. -
In
webpack.config.jswe declare an external variable@mappable-world/mappable-types, in which we specify the path to the API loader and the promisemappable.readyis 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
npmpackage@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.jsonis no longer needed thecompilerOptions.paths.mappablesetting. -
With this connection, fully loaded JS API modules become available in the assembled project
jsfile and the execution ofmappable.readyis guaranteed, so the project code can be written shorter and neater (the asynchronousinitMapfunction 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';