JSDoc комментарии

При разработке картографических приложений на основе Mapbox GL JS объём кода быстро увеличивается. Появляются пользовательские источники данных, слои, обработчики событий, вспомогательные функции для работы с геометрией, классы управления картой и сложные структуры конфигурации. Поддерживать такой код без качественной документации становится затруднительно.

JSDoc — это стандарт документирования JavaScript-кода при помощи специальных комментариев. Такие комментарии позволяют:

  • описывать назначение функций и классов;
  • документировать параметры и возвращаемые значения;
  • фиксировать структуру объектов конфигурации;
  • улучшать автодополнение в редакторах кода;
  • генерировать документацию автоматически;
  • облегчать сопровождение проектов.

Для библиотек, работающих с геоданными и картографическими объектами, JSDoc особенно полезен, поскольку многие методы принимают сложные структуры данных.


Синтаксис JSDoc-комментариев

JSDoc-комментарий начинается с последовательности /** и завершается */.

/**
 * Создает карту Mapbox.
 */
function createMap() {

}

Многострочные комментарии обычно располагаются непосредственно перед документируемым элементом.

/**
 * Устанавливает начальный масштаб карты.
 */
map.setZoom(10);

На практике JSDoc чаще применяется для документирования функций, классов, методов и объектов конфигурации.


Документирование функций

Описание функции

Простейший вариант включает краткое описание назначения функции.

/**
 * Добавляет источник GeoJSON на карту.
 */
function addGeoJsonSource() {

}

Описание должно отвечать на вопрос: что делает функция, а не как именно реализована её логика.


Тег @param

Тег @param используется для описания параметров.

/**
 * Центрирует карту по указанным координатам.
 *
 * @param {number} lng Долгота.
 * @param {number} lat Широта.
 */
function flyToLocation(lng, lat) {
    map.flyTo({
        center: [lng, lat]
    });
}

Тип указывается в фигурных скобках.

@param {string}
@param {number}
@param {boolean}
@param {Array}
@param {Object}

Несколько параметров

/**
 * Изменяет параметры отображения карты.
 *
 * @param {number} zoom Масштаб.
 * @param {number} pitch Угол наклона.
 * @param {number} bearing Поворот карты.
 */
function setView(zoom, pitch, bearing) {
    map.easeTo({
        zoom,
        pitch,
        bearing
    });
}

Документирование возвращаемого значения

Тег @returns

Если функция возвращает значение, используется тег @returns.

/**
 * Возвращает текущий масштаб карты.
 *
 * @returns {number} Текущий уровень масштабирования.
 */
function getCurrentZoom() {
    return map.getZoom();
}

Допустимы также формы:

@returns {string}
@returns {Object}
@returns {Array}

Возврат объекта

/**
 * Возвращает координаты центра карты.
 *
 * @returns {{lng:number, lat:number}}
 */
function getCenter() {
    const center = map.getCenter();

    return {
        lng: center.lng,
        lat: center.lat
    };
}

Такой подход особенно полезен при работе с географическими координатами.


Документирование объектов конфигурации

Mapbox GL JS активно использует конфигурационные объекты.

Например:

map.addLayer({
    id: 'roads',
    type: 'line',
    source: 'roads'
});

Для описания подобных структур применяется несколько тегов @property.

/**
 * Параметры слоя карты.
 *
 * @typedef {Object} LayerOptions
 * @property {string} id Идентификатор слоя.
 * @property {string} type Тип слоя.
 * @property {string} source Источник данных.
 */

После этого тип можно использовать повторно.

/**
 * Создает слой.
 *
 * @param {LayerOptions} options Настройки слоя.
 */
function createLayer(options) {

}

Использование @typedef

Создание пользовательских типов

Для работы с геоданными удобно создавать собственные типы.

/**
 * Географическая точка.
 *
 * @typedef {Object} Coordinate
 * @property {number} lng Долгота.
 * @property {number} lat Широта.
 */

Использование:

/**
 * Перемещает карту к координатам.
 *
 * @param {Coordinate} point Координаты точки.
 */
function moveTo(point) {
    map.flyTo({
        center: [point.lng, point.lat]
    });
}

Такой подход делает код значительно понятнее.


Документирование массивов

Массив примитивов

/**
 * Возвращает список идентификаторов слоев.
 *
 * @returns {string[]}
 */
function getLayerIds() {

}

Массив объектов

/**
 * @typedef {Object} MarkerData
 * @property {number} lng
 * @property {number} lat
 * @property {string} title
 */
/**
 * Создает набор маркеров.
 *
 * @param {MarkerData[]} markers
 */
function addMarkers(markers) {

}

Редактор сразу показывает структуру элементов массива.


Документирование GeoJSON-данных

GeoJSON является одним из основных форматов в экосистеме Mapbox.

Для него полезно создавать отдельные типы.

/**
 * @typedef {Object} GeoJsonFeature
 * @property {string} type
 * @property {Object} geometry
 * @property {Object} properties
 */

Использование:

/**
 * Добавляет объект на карту.
 *
 * @param {GeoJsonFeature} feature Геообъект.
 */
function addFeature(feature) {

}

Необязательные параметры

Квадратные скобки

Необязательный параметр заключается в квадратные скобки.

/**
 * Создает маркер.
 *
 * @param {number} lng Долгота.
 * @param {number} lat Широта.
 * @param {string} [color] Цвет маркера.
 */
function createMarker(lng, lat, color) {

}

Значение по умолчанию

/**
 * Создает маркер.
 *
 * @param {string} [color='red'] Цвет маркера.
 */
function createMarker(color = 'red') {

}

Такой комментарий сразу показывает стандартное значение параметра.


Объект параметров функции

Во многих случаях функция принимает единый объект настроек.

/**
 * Добавляет источник данных.
 *
 * @param {Object} options Настройки источника.
 * @param {string} options.id Идентификатор.
 * @param {string} options.url URL данных.
 * @param {boolean} options.cluster Включение кластеризации.
 */
function addSource(options) {

}

Пример вызова:

addSource({
    id: 'cities',
    url: '/data/cities.geojson',
    cluster: true
});

Документирование стрелочных функций

JSDoc одинаково работает со стрелочными функциями.

/**
 * Получает центр карты.
 *
 * @returns {mapboxgl.LngLat}
 */
const getCenter = () => {
    return map.getCenter();
};

Документирование обработчиков событий

Mapbox GL JS активно использует события.

map.on('click', (event) => {

});

Описание обработчика:

/**
 * Обрабатывает клик по карте.
 *
 * @param {mapboxgl.MapMouseEvent} event Событие клика.
 */
function handleMapClick(event) {

}

Подключение:

map.on('click', handleMapClick);

Документирование классов

Тег @class

/**
 * Класс управления пользовательскими маркерами.
 *
 * @class
 */
class MarkerManager {

}

На практике большинство современных инструментов способны определять классы автоматически, однако тег может использоваться для явного описания.


Конструктор класса

/**
 * Управляет работой маркеров.
 */
class MarkerManager {

    /**
     * @param {mapboxgl.Map} map Экземпляр карты.
     */
    constructor(map) {
        this.map = map;
    }
}

Методы класса

class MarkerManager {

    /**
     * Добавляет маркер.
     *
     * @param {number} lng
     * @param {number} lat
     */
    addMarker(lng, lat) {

    }

    /**
     * Удаляет все маркеры.
     */
    clear() {

    }
}

Документирование свойств класса

class LayerManager {

    /**
     * Экземпляр карты.
     *
     * @type {mapboxgl.Map}
     */
    map;

    constructor(map) {
        this.map = map;
    }
}

Тег @example

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

/**
 * Создает слой линий.
 *
 * @param {string} sourceId Идентификатор источника.
 *
 * @example
 * createLineLayer('roads');
 */
function createLineLayer(sourceId) {

}

Пример часто оказывается полезнее длинного описания.


Тег @throws

Используется для описания возможных исключений.

/**
 * Возвращает слой по идентификатору.
 *
 * @param {string} id Идентификатор слоя.
 * @returns {Object}
 * @throws {Error} Если слой не найден.
 */
function getLayer(id) {

}

Тег @deprecated

Позволяет отметить устаревший API.

/**
 * @deprecated Использовать createMarkerV2().
 */
function createMarker() {

}

Редакторы кода обычно подсвечивают подобные методы как устаревшие.


Тег @see

Используется для указания связанных сущностей.

/**
 * Создает источник данных.
 *
 * @see createLayer
 */
function createSource() {

}

Тег @since

Фиксирует версию появления функциональности.

/**
 * @since 2.0.0
 */
function addClusterLayer() {

}

Документирование перечислений

В картографических приложениях часто используются фиксированные наборы значений.

/**
 * Типы слоев.
 *
 * @readonly
 * @enum {string}
 */
const LayerType = {
    FILL: 'fill',
    LINE: 'line',
    SYMBOL: 'symbol',
    CIRCLE: 'circle'
};

Использование:

/**
 * @param {LayerType} type
 */
function createLayer(type) {

}

Документирование асинхронных функций

Mapbox-приложения часто получают данные по сети.

/**
 * Загружает GeoJSON-файл.
 *
 * @param {string} url Адрес файла.
 * @returns {Promise<Object>}
 */
async function loadGeoJson(url) {
    const response = await fetch(url);

    return response.json();
}

Документирование промисов

Более подробный вариант:

/**
 * Получает данные объектов.
 *
 * @returns {Promise<GeoJsonFeature[]>}
 */
function loadFeatures() {

}

Типизация Mapbox GL JS через JSDoc

Современные редакторы понимают типы Mapbox GL JS.

/**
 * @type {mapboxgl.Map}
 */
let map;

Тип события карты

/**
 * @param {mapboxgl.MapMouseEvent} event
 */
function onClick(event) {

}

Тип маркера

/**
 * @type {mapboxgl.Marker}
 */
let marker;

Тип всплывающего окна

/**
 * @type {mapboxgl.Popup}
 */
let popup;

Тип географических координат

/**
 * @type {mapboxgl.LngLat}
 */
let center;

Импорт типов через @typedef и import

При использовании модульной архитектуры можно импортировать типы из других файлов.

/**
 * @typedef {import('./types').Coordinate} Coordinate
 */

Далее тип используется как обычный пользовательский тип.

/**
 * @param {Coordinate} point
 */
function flyToPoint(point) {

}

Документирование модулей

Для крупных проектов удобно документировать целые модули.

/**
 * Работа со слоями карты.
 *
 * @module LayerManager
 */

Такой подход особенно полезен при автоматической генерации документации.


Практика оформления JSDoc в проектах Mapbox GL JS

Качественные комментарии обладают несколькими характерными признаками:

  • описание отвечает на вопрос о назначении сущности;
  • параметры имеют понятные имена;
  • типы указаны максимально точно;
  • сложные объекты оформлены через @typedef;
  • примеры показывают реальные сценарии использования;
  • устаревшие элементы помечаются через @deprecated;
  • возвращаемые значения документируются всегда;
  • комментарии поддерживаются в актуальном состоянии вместе с кодом.

В крупных картографических приложениях JSDoc превращается не просто в средство документирования, а в дополнительный уровень типизации и самодокументирования архитектуры, позволяющий эффективно сопровождать кодовую базу Mapbox GL JS даже при большом количестве источников данных, слоев, обработчиков событий и пользовательских компонентов управления картой.