JSDoc: Надёжная альтернатива TypeScript
- Typescript
- Что такое JSDoc
- JSDoc vs Typescript
- Как использовать JSDoc: Основы
- Конвертирование файлов JSDoc
- Заключение
- Ресурсы
JavaScript закрепился в качестве одного из самых используемых скриптовых языков в последнее время. Он известен своей простотой в написании скриптов на веб-платформе. По мере развития языка он прошёл путь от "игрушечного" языка, использовавшего успех Java, до полноценного языка, используемого для создания не только небольших скриптов.
К сожалению, это выявляет недостатки языка. К ним относятся:
- Отсутствие статической типизации и строгой проверки типов: Javascript всепрощающ: аргументы могут быть переданы в функции, которые их не принимают, необходимые значения могут быть опущены и т. д. Это не относится к статически типизированным языкам, так как они будут выдавать ошибки во время компиляции. В приложениях на Javascript эти ошибки могут проникать в продакшн.
- Сложность масштабирования и сопровождения больших кодовых баз: JavaScript не предоставляет надёжных механизмов для управления большими кодовыми базами, что затрудняет масштабирование и поддержку проектов с течением времени.
Typescript
В 2014 году компания Microsoft выпустила Typescript v1.0. Это изменило всю экосистему JavaScript.
TypeScript, который является надмножеством JavaScript, решил все вышеперечисленные проблемы и даже больше. Это делало его все более и более популярным даже в последнее время.
State of Js 2022 показывает повышение уровня использования TypeScript.
TypeScript, решив множество проблем, не обошёлся без недостатков.
В этой статье мы рассмотрим очень хорошую альтернативу TypeScript под названием JSDoc, решающую проблемы статической типизации и масштабируемости, а также устраняющую недостатки TypeScript для экосистемы JavaScript.
Что такое JSDoc
JSDoc — это система документации для JavaScript, помогающая в работе с кодом JavaScript. Она работает с помощью комментариев, содержащих синтаксис JSDoc.
Синтаксис JSDoc служит для различных целей, включая аннотирование значений типами, указание типов параметров и типов возврата для функций, документирование и предоставление информации об использовании функций, ошибок ввода и т. д. В дальнейшем, подобно TypeScript, они могут быть использованы редакторами кода в качестве руководства для программистов, создающих, использующих или поддерживающих указанную кодовую базу.
JSDoc vs Typescript
JSDoc и TypeScript решают проблемы с написанием и поддержкой обычного JavaScript-кода. Однако они используют разные подходы, имеющие свои преимущества и недостатки.
Преимущества JSDoc по сравнению с Typescript
- Гибкость и совместимость: JSDoc — это просто комментарии JavaScript, а значит, его можно добавить в любую кодовую базу JavaScript, независимо от версии языка, и он не привязан к компилятору, как TypeScript.
- Аннотация кода: JSDoc можно использовать не только для проверки типов. Его можно использовать для добавления дополнительной документации, описания работы функций, генерации веб-сайта документации — все это повышает удобство сопровождения и понимания кода.
- Отсутствие этапа компиляции: Это одна из самых мотивирующих причин перехода на JSDoc с TypeScript. TypeScript требует компиляции для преобразования Typescript кода в JavaScript, чтобы браузер мог его понять, в то время как JSDoc не требует никаких других шагов, поскольку это просто "комментарии", которые являются поддерживаемой функцией самого JavaScript. Это может упростить и увеличить скорость рабочего процесса разработки по сравнению с использованием необходимого конвейера сборки Typescript каждый раз, когда вы вносите изменения.
Преимущества Typescript по сравнению с JSDoc
Хотя JSDoc имеет массу преимуществ перед TypeScript, есть причины, по которым использование Typescript со временем становится все более популярным. Вот некоторые из преимуществ Typescript по сравнению с JSDoc:
- Более сильная статическая типизация: TypeScript предоставляет надёжную модель типов и отлавливает эти ошибки во время компиляции. В отличие от JSDoc, где типизация заканчивается в самом коде и не соблюдается.
- Выведение типа: TypeScript может выводить тип по его значению. Это помогает сократить количество явных аннотаций типов и делает кодовую базу менее многословной.
- Транспиляция: TypeScript может перенимать новейшие и будущие возможности языка JavaScript благодаря возможности полифилла. Она эффективно транспилирует код до версии, понятной браузерам, не поддерживающим эти функции.
Как использовать JSDoc: Основы
Благодаря своей долговечности JSDoc имеет широкую поддержку во всех современных редакторах и может быть использован "из коробки" без установки.
Для добавления JSDoc в .js-файл, который, как заявлено, является просто комментарием, нужно открыть комментарий с дополнительным символом *
.
// Нормальный Javascript Комментарий 1
/* Нормальный комментарий 2 */
/**
JSDoc, содержащий две звёздочки
*/
Вот несколько основных функций для начала работы.
Добавление описания кода к блоку кода
/** Название языка, для которого написан JSDoc*/
const language = "JavaScript"
Указание типов значений
/**
* Представляет автора этой статьи
* @type {string}
*/
const writerName = "Elijah"
Теперь указывается, что переменная username
должна быть типа string
.
Указание типа объектов и массивов
/**
* @type {Array<string>}
*/
const colours = ['red', 'blue', 'green']
/**
* @type {number[]}
*/
const primeNumbers = [1, 2, 3, 5, 7]
Оба метода допустимы в JSDoc (так же, как и в Typescript).
Указать тип объекта можно с помощью директивы @typedef
.
/**
* * @typedef {Object} User - Схема User
* @property {number} id
* @property {string} username
* @property {string} email
* @property {Array<number>} postLikes
* @property {string[]} friends
*/
/**@type {User} */
const person1 = {
id: 847,
username: "Elijah",
email: "elijah@user.com",
postLikes: [44, 22, 24, 39],
friends: ['fede', 'Elijah']
}
/** @type {User} */
const person2 = {
id: 424,
username: "Winston",
email: "winston@user.com",
postLike: [18, 53, 98],
friends: ['Favour', 'Jane']
}
Типизация функций (параметры, возврат и ожидаемые типы ошибок)
/**
* Деление двух чисел.
* @param {number} dividend - Делимое.
* @param {number} divisor - Делитель.
* @returns {number} Результат деления.
*/
function divideNumbers(dividend, divisor) {
return dividend/divisor;
}
Ключевое слово @param
, за которым следует определение типа, представляет собой значение, принимаемое определяемой функцией. Вы также можете добавить описание того, что представляет собой параметр, после дефиса (-
).
Ключевое слово @returns
используется для определения возвращаемого функцией результата. Это особенно полезно для больших функций. Может быть сложно просмотреть весь код, включая ранние возвраты, чтобы определить, что ожидается от функции.
Кроме того, вы можете добавить возможные ошибки, которые может выкинуть функция, используя директиву @throws
.
Улучшив функцию деления, мы могли бы указать, что она возвращает ошибку, если делитель равен нулю, а также обработать это в самом коде.
/**
* Деление двух чисел.
* @param {number} dividend - Делимое.
* @param {number} divisor - Делитель.
* @returns {number} Результат деления.
* @throws {ZeroDivisionError} Аргумент делителя должен быть ненулевым
*/
function divideNumbers(dividend, divisor) {
if (divisor === 0) {
throw new DivisionByZeroError('Cannot Divide by zero')
}
return dividend/divisor;
}
@throws
может принимать либо тип ошибки (ZeroDivisionError
), либо описание (Argument divisor must...
), либо оба.
/**
* Пользовательская ошибка при делении на ноль.
*/
class DivisionByZeroError extends Error {
constructor(message = "Cannot Divide By Zero") {
super(message);
this.name = "DivisionByZeroError";
}
}
Поскольку JavaScript не заставляет вас обрабатывать ошибки, вы должны это делать, поскольку это помогает улучшить совместную работу и сопровождение.
Полная типизация классов (описание, конструктор и методы)
Если пойти ещё дальше, то можно также полностью типизировать синтаксис класса с помощью JSDoc.
/**
* Класс Rectangle
* @class
* @classdec Четырёхсторонний многоугольник с противоположными сторонами равной длины и четырьмя прямыми углами
*/
class Rectangle {
/**
* Инициализация объекта Rectangle.
* @param {number} length - Длина прямоугольника.
* @param {number} width - Ширина прямоугольника.
*/
constructor(length, width) {
this.length = length;
this.width = width;
}
/**
* Вычисление площади прямоугольника.
* @returns {number} Площадь прямоугольника.
*/
calculateArea() {
return this.length * this.width;
}
/**
* Вычисление периметра прямоугольника.
* @returns {number} Периметр прямоугольника.
*/
calculatePerimeter() {
return 2 * (this.length + this.width);
}
}
Выше представлен класс простого прямоугольника с двумя методами для вычисления его площади и периметра.
Ключевое слово @class
используется, чтобы показать, что функция должна быть вызвана с помощью ключевого слова new
. @classdec
используется для описания всего класса. При типизации классов важно идти дальше, добавляя типы и описания к:
- Конструктору.
- Всем методам и переменным, созданным в классе.
Мы использовали ключевое слово @params
, для указания типов и описания аргументов, которые необходимо передать в функцию конструктора. Методы в классе типизируются так же, как и функции, о чем мы говорили в предыдущем разделе.
Улучшение общей документации кода
Помимо добавления в код необходимых типов, существует множество способов, с помощью которых JSDoc помогает улучшить читабельность и простоту понимания. Вот несколько из них:
Добавление авторов кода: Автор фрагмента может быть добавлен с помощью директивы
@author
с указанием имени и электронной почты автора/**
* Возможное название для этой статьи
* @type {string}
* @author Elijah [elijah@example.com]
*/
const articleTitle = "Demystifying JSDoc"Пример использования: Вы также можете добавить фрагменты кода, показывающие, как следует использовать тот или иной блок кода. Это особенно полезно для сложных блоков кода.
/**
* Суммы квадратов двух чисел a**2 + b**2
* @example <caption>Как использовать функцию sumSquares</caption>
* // возвращает 13
* sumSquares(2, 3)
* @example
* // возвращает 41
* sumSquares(4, 5)
* // Типы функции
* @param {number} a - Первое число
* @param {number} b - Второе число
* @returns {Number} Возвращает сумму квадратов
* */
const sumSquares = function(a, b){
return a**2 + b**2
}Для этого используется директива
@example
. Кроме того, это можно сделать с помощью тегаcaption
.Версионирование: Вы также можете указать версию элемента с помощью директивы
@version
./**
* @version 1.0.0
* @type {number}
* */
const meaningOfLife = 42Полезные ссылки: Часто бывает, что вы хотите указать пользователю на место, где он может получить больше знаний о коде. Это может быть репозиторий GitHub, какое-нибудь руководство, блог и т. д. В этом вам помогут две директивы.
@link
и@tutorial
./**
* Как использовать теги ссылок
* Также смотрите {@link https://jsdoc.app/tags-inline-link.html официальную документацию} для получения дополнительной информации
* @tutorial getting-started
* */
function myFunction (){
}Тег
@link
отображает официальную документацию в виде ссылки на указанную ссылку. Он используется для создания ссылки на указанный вами URL, в то время как тег@tutorial
используется для направления пользователя на относительную ссылку руководства в сгенерированных документах.Создание модулей: Создать модуль в JSDoc можно с помощью тега
@module
в верхней части файла. Это делает текущий файл модулем. Модули группируются в отдельный раздел на сайте сгенерированной документации.// jsdoc.js
/** @module firstDoc */
//Остальная часть кода находится здесь
Конвертирование файлов JSDoc
Одним из самых больших плюсов использования JSDoc является возможность конвертировать файлы JSDoc для создания веб-сайта документации или даже в Typescript, чтобы воспользоваться преимуществами использования Typescript, такими как отлов ошибок во время компиляции, интеграция с проектами Typescript и т. д.
Генерирование веб-сайта документации из файлов JSDoc
Как было сказано выше, вы можете создать более читабельный GUI, выполнив следующие действия:
Установить jsdoc
npm install -g jsdoc
Запустите jsdoc для целевого файла
jsdoc path/to/file.js
Откройте сгенерированный сайт. В jsdoc CLI автоматически создаётся папка out
, в которой хранятся файлы. Перейдите в папку out/index.html
и откройте её в браузере.
Генерация .d.ts
файлов из JSDoc
Файлы .d.ts
в TypeScript представляют собой файлы деклараций, содержащие типы, доступные всем файлам .ts
в проекте. Вы можете сгенерировать эти файлы из кода JSDoc, выполнив следующие действия:
Установите tsd-jsdoc
в папку проекта
npm install tsd-jsdoc
Сгенерируйте .d.ts
файлы
Для отдельного файла
jsdoc -t node_modules/tsd-jsdoc/dist -r our/jsdoc/file/path.js
Для нескольких файлов
jsdoc -t node_modules/tsd-jsdoc/dist -r file1.js file2.js file3.js ...
Для всей директории
jsdoc -t node_modules/tsd-jsdoc/dist -r src
Он объединяет все типы из файла(ов) в один файл out/types.d.ts
.
Примечание: Здесь предполагается, что вы установили jsdoc
из предыдущего раздела. Если нет, установите его перед выполнением этого шага.
Заключение
На данном этапе мы изучили основы использования JSDoc, а также генерацию типов и сайтов документации из JSDoc кода. JSDoc особенно полезен в тех случаях, когда время компиляции/шаг сборки Typescript оказывает негативное влияние на производительность. Он также полезен при работе с унаследованной кодовой базой.
Рич Харрис (Rich Harris, создатель Svelte и SvelteKit) перенёс весь репозиторий Svelte и SvelteKit с TypeScript на JSDoc. TypeScript также добавил поддержку многих деклараций JSDoc (источник)
Ресурсы
- Полный исходный код из этой статьи — Github
- JSDoc official documentation
- Moving Svelte 4 from TS to JSDoc — Rich Harris