Подключение API на TypeScript

Обычное подключение

<!DOCTYPE html>
<html>
  <head>
    <!-- Вместо YOUR_API_KEY подставить значение настоящего ключа -->
    <script src="https://api-maps.yandex.ru/v3/?apikey=YOUR_API_KEY&lang=ru_RU"></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: [37.623082, 55.75254],
        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": [
      "./node_modules/@types",
      "./node_modules/@yandex/ymaps3-types"
    ],
    "paths": {
      "ymaps3": [
        "./node_modules/@yandex/ymaps3-types"
      ]
    }
  }
}
{
    "devDependencies": {
        "@yandex/ymaps3-types": "^0.0.27",
        "http-server": "14.1.1",
        "typescript": "5.2.2"
    },
    "scripts": {
        "compile": "./node_modules/.bin/tsc",
        "start": "npx http-server ."
    }
}

Поставьте зависимости, скомпилируйте typescript и запустите локальный сервер:

npm install
npm run compile
npm run start

Откройте приложение

Особенности обычного подключения

  1. В package.json добавляем dev-зависимость от пакета @yandex/ymaps3-types.

    Рекомендуется устанавливать последнюю версию:

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

  2. В tsconfig.json задаём compilerOptions.typeRoots со списком путей к файлам типов. Добавляем туда путь к пакету @yandex/ymaps3-types, благодаря чему в глобальной области видимости появляется пространство имен ymaps3 с типами.

    Примечание

    Пространство имен ymaps3 содержит все типы классов, которые предоставляет JS API, но до резолва ymaps3.ready в среде выполнения они недоступны.

  3. В tsconfig.json задаём compilerOptions.paths, в которой сообщаем ts-компилятору о том, что при импорте пакета ymaps3 его контент следует искать по указанному пути. Благодаря этому в проектных файлах можно импортировать типы словно они лежат не в @yandex/ymaps3-types, а в пакете ymaps3:

    import type { YMapLocationRequest } from 'ymaps3';
    

    Все типы должны быть импортированы из корня.

    Внутренняя структура не гарантирована и может меняться со временем.

  4. В теге script, который загружает скомплированный проектный js, указываем атрибут type="module", чтобы браузер активировал поддержку ESM в js-файлах.

    Если в примере этого не сделать, возникнет ошибка:

    SyntaxError: Unexpected token 'export {}'

Подключение через top-level-await (рекомендуется)

<!DOCTYPE html>
<html>
  <head>
    <!-- Вместо YOUR_API_KEY подставить значение настоящего ключа -->
    <script src="https://api-maps.yandex.ru/v3/?apikey=YOUR_API_KEY&lang=ru_RU"></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'
import { YMap, YMapDefaultSchemeLayer } from './lib/ymaps.js'

const LOCATION: YMapLocationRequest = {
    center: [37.588144, 55.733842],
    zoom: 9
};

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

map.addChild(new YMapDefaultSchemeLayer());
await ymaps3.ready;

export const {YMap, YMapDefaultSchemeLayer} = ymaps3;
{
  "compilerOptions": {
    "target": "es2017",
    "lib": [
      "dom",
      "dom.iterable",
      "esnext"
    ],
    "esModuleInterop": true,
    "module": "esnext",
    "moduleResolution": "node",
    "typeRoots": [
      "./node_modules/@types",
      "./node_modules/@yandex/ymaps3-types"
    ],
    "paths": {
      "ymaps3": [
        "./node_modules/@yandex/ymaps3-types"
      ]
    }
  }
}
{
    "devDependencies": {
        "@yandex/ymaps3-types": "^0.0.27",
        "http-server": "14.1.1",
        "typescript": "5.2.2"
    },
    "scripts": {
        "compile": "./node_modules/.bin/tsc",
        "start": "npx http-server ."
    }
}

Поставьте зависимости, скомпилируйте typescript и запустите локальный сервер:

npm install
npm run compile
npm run start

Откройте приложение

Особенности

  1. В теге script, который загружает скомпилированный проектный js, указываем атрибут type="module", чтобы активировать поддержку ECMAScript Modules (ESM) и top-level-await:

    <script type="module" src="index.js"></script>
    
    Поддержка ESM при использовании бандлеров

    Если для сборки проекта используется бандлер, например Webpack, то в файл package.json необходимо добавить "type": "module"

  2. В tsconfig.json, для корректной работы top-level-await, параметр compilerOptions.module должен быть установлен в одно из следующих значений: es2022, esnext, system или preserve. Также параметр compilerOptions.target должен быть es2017 или выше.

  3. В файле lib/ymaps.ts дожидаемся полной загрузки JS API, после чего экспортируем необходимые компоненты карты для их использования в других частях проекта:

    await ymaps3.ready;
    
    export const {YMap, YMapDefaultSchemeLayer} = ymaps3;
    
  4. Использование top-level-await в lib/ymaps.ts гарантирует выполнение ymaps3.ready до импорта компонентов карты, поэтому проектный код можно писать короче и опрятнее (больше не нужна асинхронная функция initMap):

    import { YMap, YMapDefaultSchemeLayer } from './lib/ymaps.js'
    
    const map = new YMap({...});
    

Подключение с Webpack

Способ №1

<!DOCTYPE html>
<html>
    <head>
        <!-- Вместо YOUR_API_KEY подставить значение настоящего ключа -->
        <script src="https://api-maps.yandex.ru/v3/?apikey=YOUR_API_KEY&lang=ru_RU"></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: [37.623082, 55.75254],
    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": [
      "./node_modules/@types",
      "./node_modules/@yandex/ymaps3-types"
    ],
    "paths": {
      "ymaps3": [
        "./node_modules/@yandex/ymaps3-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": {
    "@yandex/ymaps3-types": "^0.0.27",
    "http-server": "14.1.1",
    "typescript": "5.2.2",
    "webpack": "5.88.2",
    "webpack-cli": "5.1.4"
  }
}

Поставьте зависимости, скомпилируйте typescript, соберите проект и запустите локальный сервер:

npm install
npm run compile
npm run build
npm run start

Откройте приложение

Этот способ ничем не отличается от предыдущего подключения. Добавляется только шаг сборки из проектных js-файлов в единый build/bundle.js

Способ №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: [37.623082, 55.75254],
  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": [
      "./node_modules/@types",
      "./node_modules/@yandex/ymaps3-types"
    ]
  },
}
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=ru_RU";
            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.27",
    "http-server": "14.1.1",
    "typescript": "5.2.2",
    "webpack": "5.88.2",
    "webpack-cli": "5.1.4"
  }
}

Поставьте зависимости, скомпилируйте typescript, соберите проект и запустите локальный сервер:

npm install
npm run compile
npm run build
npm run start

Откройте приложение

Особенности

  1. Больше не нужно в шапке HTML-страницы подключать тег <script>. Этим занимается сам webpack.

  2. В webpack.config.js объявляем external-переменную @yandex/ymaps3-types, в которой указываем путь к загрузчику API и там же происходит разрешение промиса ymaps3.ready.

    Благодаря этому в проектном коде появляется возможность импортировать типы и классы JS API так, словно код поставляется не через глобальную переменную, а через npm-пакет @yandex/ymaps3-types:

    // Импорт типов
    import type { YMapLocationRequest } from '@yandex/ymaps3-types';
    
    // Импорт runtime-кода
    import {YMap, YMapDefaultSchemeLayer} from '@yandex/ymaps3-types';
    
  3. В tsconfig.json больше не нужна настройка compilerOptions.paths.ymaps3.

  4. При таком подключении в собранном проектном js-файле становятся доступны полностью загруженные модули JS API и гарантируется исполнение ymaps3.ready, поэтому проектный код можно писать короче и опрятнее (больше не нужна асинхронная функция initMap):

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

Настройка alias

Обратите внимание, в последнем примере импорт происходит из пакета с названием @yandex/ymaps3-types. Это название можно изменить с помощью алиаса в package.json:

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

После этого не забудьте обновите имя external-переменной в webpack.config.js:

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

После всех изменений импорт типов и классов можно сделать из пакета @yandex/ymaps3:

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

Примеры

Сodesandbox

Предыдущая
Следующая