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://api-maps.yandex.ru/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 { YMapLocationRequest } from 'ymaps3';

async function initMap(): Promise<void> {
    await ymaps3.ready;

    const LOCATION: YMapLocationRequest = {
        center: [25.229762, 55.289311],
        zoom: 9
    };

    const { YMap, YMapDefaultSchemeLayer } = ymaps3;

    const map = new YMap(document.getElementById('app'), { location: LOCATION });
    map.addChild(new YMapDefaultSchemeLayer({}));
}

initMap();
{
  "compilerOptions": {
    "target": "es2015",
    "lib": [
      "dom",
      "dom.iterable",
      "esnext"
    ],
    "esModuleInterop": true,
    "moduleResolution": "node",
    "typeRoots": [
      "./types",
      "./node_modules/@types",
      "./node_modules/@yandex/ymaps3-types"
    ],
    "paths": {
      "ymaps3": [
        "./node_modules/@yandex/ymaps3-types"
      ]
    }
  }
}
import { YMap } from 'ymaps3';

declare let map: YMap;
{
    "devDependencies": {
        "@yandex/ymaps3-types": "^0.0.17",
        "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

  1. In package.json adding a dev-dependency on the package @yandex/ymaps3-types.

    It is recommended to install the latest version:

    npm i --save-dev @yandex/ymaps3-types@latest

  2. In tsconfig.json we set CompilerOptions.typeRoots with a list of paths to file types. Adding the path to the package @yandex/ymaps3-types there, so that the namespace ymaps3 with types appears in the global scope.

    Note

    The namespace ymaps3 contains all the class types that the JS API provides, but they are not available in the runtime until the resolving ymaps3.ready.

  3. In tsconfig.json we set CompilerOptions.paths, in which we inform the ts compiler that when importing the package ymaps3, 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 @yandex/ymaps3-types, but in the ymaps3 package:

    import type { YMapLocationRequest } from 'ymaps3';
    

    All types must be imported from the root.

    The internal structure is not guaranteed and may change over time.

  4. In the script tag that loads the compiled project js-file, specify the type="module" attribute so that the browser activates support for ESM in js files.

    If this is not done in the example, an error will occur:

    SyntaxError: Unexpected token 'export {}'

Connecting with Webpack

Method 1

<!DOCTYPE html>
<html>
    <head>
        <!-- Substitute the value of the real key instead of YOUR_API_KEY -->
        <script src="https://api-maps.yandex.ru/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 YMapLocationRequest } from 'ymaps3';

async function initMap() {
  await ymaps3.ready;

  const LOCATION: YMapLocationRequest = {
    center: [25.229762, 55.289311],
    zoom: 9
  };

  const { YMap, YMapDefaultSchemeLayer } = ymaps3;

  const map = new YMap(document.getElementById('app'), {location: LOCATION});
  map.addChild(new YMapDefaultSchemeLayer({}));
}

initMap();
{
  "compilerOptions": {
    "target": "es2015",
    "lib": [
      "dom",
      "dom.iterable",
      "esnext"
    ],
    "esModuleInterop": true,
    "moduleResolution": "node",
    "typeRoots": [
      "./types",
      "./node_modules/@types",
      "./node_modules/@yandex/ymaps3-types"
    ],
    "paths": {
      "ymaps3": [
        "./node_modules/@yandex/ymaps3-types"
      ]
    }
  },
}
import { YMap } from 'ymaps3';

declare let map: YMap;
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": {
    "@yandex/ymaps3-types": "^0.0.17",
    "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 { YMapLocationRequest } from '@yandex/ymaps3-types';
import {YMap, YMapDefaultSchemeLayer} from '@yandex/ymaps3-types';

const LOCATION: YMapLocationRequest = {
  center: [25.229762, 55.289311],
  zoom: 9
};

const map = new YMap(document.getElementById('app'), {location: LOCATION});
map.addChild(new YMapDefaultSchemeLayer({}));
{
  "compilerOptions": {
    "target": "es2015",
    "lib": [
      "dom",
      "dom.iterable",
      "esnext"
    ],
    "esModuleInterop": true,
    "moduleResolution": "node",
    "typeRoots": [
      "./types",
      "./node_modules/@types",
      "./node_modules/@yandex/ymaps3-types"
    ]
  },
}
import { YMap } from '@yandex/ymaps3-types';

declare let map: YMap;
const path = require('path');

module.exports = {
    mode: 'development',
    entry: './index.js',
    output: {
        filename: 'bundle.js',
        path: path.resolve(__dirname, 'build'),
    },
    externals: {
      '@yandex/ymaps3-types': [
        `promise new Promise((resolve) => {
            if (typeof ymaps3 !== 'undefined') {
              return ymaps3.ready.then(() => resolve(ymaps3));
            }

            const script = document.createElement('script');
            script.src = "https://api-maps.yandex.ru/v3/?apikey=YOUR_API_KEY&lang=en_US";
            script.onload = () => {
              ymaps3.ready.then(() => resolve(ymaps3));
            };
            document.head.appendChild(script);
          })`
      ]
    },
    devtool: 'cheap-source-map'
};
{
  "scripts": {
    "compile": "./node_modules/.bin/tsc",
    "build": "webpack",
    "start": "npx http-server ."
  },
  "devDependencies": {
    "@yandex/ymaps3-types": "^0.0.17",
    "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

  1. It is no longer necessary to connect the tag <script> in the header of the HTML page. This is done by webpack itself.

  2. In webpack.config.js we declare an external variable @yandex/ymaps3-types, in which we specify the path to the API loader and the promise ymaps3.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 @yandex/ymaps3-types:

    // Importing Types
    import type { YMapLocationRequest } from '@yandex/ymaps3-types';
    
    // Importing the runtime code
    import {YMap, YMapDefaultSchemeLayer} from '@yandex/ymaps3-types';
    
  3. In tsconfig.json is no longer needed the compilerOptions.paths.ymaps3 setting.

  4. With this connection, fully loaded JS API modules become available in the assembled project js file and the execution of ymaps3.ready is guaranteed, so the project code can be written shorter and neater (the asynchronous initMap function is no longer needed):

    import type { YMapLocationRequest } from '@yandex/ymaps3-types';
    import {YMap, YMapDefaultSchemeLayer} from '@yandex/ymaps3-types';
    
    const {YMap} = ymaps3;
    const map = new YMap({...});
    

Alias

Note that in the last example, the import comes from a package named @yandex/ymaps3-types. This name can be changed using the alias in package.json:

{
  "devDependencies": {
    // ...
    "@yandex/ymaps3": "npm:@yandex/ymaps3-types@0.0.17",
    // ...
  }
}

Don't forget to update the name of the external variable in webpack.config.js:

module.exports = {
  //...
  externals: {
    '@yandex/ymaps3': [
      // ...
    ]
  }
}

Don't forget to change the package name to types/index.d.ts:

import { YMap } from '@yandex/ymaps3';

declare let map: YMap;

After all the changes, you can import types and classes from the package @yandex/ymaps3:

import type { YMapLocationRequest } from '@yandex/ymaps3';
import {YMap, YMapDefaultSchemeLayer} from '@yandex/ymaps3';

Examples

Codesandbox