JSDoc — это стандарт документирования JavaScript‑кода с помощью специальных комментариев. В сочетании с TypeScript он позволяет:
- добавлять аннотации типов в
.js-файлы без их конвертации в.ts; - получать проверку типов на уровне TypeScript;
- сохранять читаемость кода и его документацию в одном месте.
Ключевое преимущество - вы получаете типобезопасность TypeScript, оставаясь в рамках синтаксиса JavaScript.
Это идеальное решение для постепенной миграции кода или, когда вы хотите обеспечить типобезопасность в проектах на JavaScript.
Как настроить JSDoc для TypeScript
Установите TypeScript (если ещё не установлен):
npm install --save-dev typescript # или yarn add --dev typescriptСоздайте
tsconfig.jsonс необходимыми опциями:{ "compilerOptions": { "allowJs": true, // разрешать JavaScript-файлы "checkJs": true, // проверять типы в .js-файлах "noEmit": true // не компилировать, только проверять }, "include": ["**/*.js"] // проверять все .js-файлы }- Добавьте
// @ts‑checkв начало каждого.js-файла, где нужна проверка типов.
Пример: JSDoc для типобезопасности
// @ts-check
/**
* Складывает два числа.
* @param {number} a
* @param {number} b
* @returns {number}
*/
function add(a, b) {
return a + b;
}
Основные аннотации JSDoc
Ниже — ключевые теги JSDoc.
@param— тип параметра функции:/** * @param {string} name — Имя пользователя * @param {number=} age — Возраст (опционально) */ function greet(name, age) { // ... }@returns— тип возвращаемого значения:/** * @returns {boolean} — Истинность условия */ function isValid() { return true; }@type— тип переменной или выражения:/** @type {string[]} */ const tags = ['js', 'ts', 'doc'];@typedef— определение сложного типа (аналогinterfaceилиtypeв TS):/** * @typedef {Object} User * @property {number} id — ID пользователя * @property {string} username — Имя * @property {boolean} [active] — Активен ли (опционально) */ /** @type {User} */ const user = { id: 1, username: 'alice' };@callback— тип функции обратного вызова:/** * @callback Processor * @param {string} input — Входные данные * @returns {string} — Обработанный результат */@template— обобщённые типы:/** * @template T * @param {T} value — Любое значение * @returns {T} — То же значение */ function identity(value) { return value; }@enum— перечисления:/** @enum {string} */ const Color = { RED: 'red', BLUE: 'blue' };
Объекты и интерфейсы
Встроенные типы объектов
// @ts-check
/**
* @param {{ firstName: string, lastName: string, age?: number }} person
*/
function greet(person) {
return `Hello, ${person.firstName} ${person.lastName}`;
}
greet({ firstName: 'John', lastName: 'Doe' }); // OK
greet({ firstName: 'Jane' }); // Ошибка: отсутствует свойство 'lastName'
Использование @typedef для сложных типов
// @ts-check
/**
* @typedef {Object} User
* @property {number} id — ID пользователя
* @property {string} username — Имя пользователя
* @property {string} [email] — Необязательный адрес электронной почты
* @property {('admin'|'user'|'guest')} role — Роль пользователя
* @property {() => string} getFullName — Метод, возвращающий полное имя
*/
/** @type {User} */
const currentUser = {
id: 1,
username: 'johndoe',
role: 'admin',
getFullName() {
return 'John Doe';
}
};
// TypeScript предоставит автодополнение для свойств User
console.log(currentUser.role);
Расширение типов
// @ts-check
/** @typedef {{ x: number, y: number }} Point */
/**
* @typedef {Point & { z: number }} Point3D
*/
/** @type {Point3D} */
const point3d = { x: 1, y: 2, z: 3 };
// @ts-expect-error — отсутствует свойство z
const point2d = { x: 1, y: 2 };
Типы функций
Объявления функций
// @ts-check
/**
* Вычисляет площадь прямоугольника.
* @param {number} width — Ширина прямоугольника
* @param {number} height — Высота прямоугольника
* @returns {number} Вычисленная площадь
*/
function calculateArea(width, height) {
return width * height;
}
// TypeScript знает типы параметров и возвращаемое значение
const area = calculateArea(10, 20);
Функциональные выражения и обратные вызовы
// @ts-check
/**
* @callback StringProcessor
* @param {string} input
* @returns {string}
*/
/**
* @type {StringProcessor}
*/
const toUpperCase = (str) => str.toUpperCase();
/**
* @param {string[]} strings
* @param {StringProcessor} processor
* @returns {string[]}
*/
function processStrings(strings, processor) {
return strings.map(processor);
}
const result = processStrings(['hello', 'world'], toUpperCase);
// result будет ['HELLO', 'WORLD']
Перегрузки функций
// @ts-check
/**
* @overload
* @param {string} a
* @param {string} b
* @returns {string}
*/
/**
* @overload
* @param {number} a
* @param {number} b
* @returns {number}
*/
/**
* @param {string | number} a
* @param {string | number} b
* @returns {string | number}
*/
function add(a, b) {
if (typeof a === 'string' || typeof b === 'string') {
return String(a) + String(b);
}
return a + b;
}
const strResult = add('Hello, ', 'World!'); // строка
const numResult = add(10, 20); // число
Продвинутые типы
Объединения и пересечения типов
// @ts-check
/** @typedef {{ name: string, age: number }} Person */
/** @typedef {Person & { employeeId: string }} Employee */
/** @typedef {Person | { guestId: string, visitDate: Date }} Visitor */
/** @type {Employee} */
const employee = {
name: 'Alice',
age: 30,
employeeId: 'E123'
};
/** @type {Visitor} */
const guest = {
guestId: 'G456',
visitDate: new Date()
};
/**
* @param {Visitor} visitor
* @returns {string}
*/
function getVisitorId(visitor) {
if ('guestId' in visitor) {
return visitor.guestId; // TypeScript знает, что это гость
}
return visitor.name; // TypeScript знает, что это Person
}
Преобразованные и условные типы
// @ts-check
/** * @template T * @typedef {[K in keyof T]: T[K] extends Function ? K : never}[keyof T] MethodNames */
/** * @template T * @typedef {{ * [K in keyof T as `get${'<' }Capitalize<string & K>{'>'}`]: () => T[K] * }} Getters */
/** @type {Getters<{ name: string, age: number }> } */
const userGetters = {
getName: () => 'John',
getAge: () => 30
};
// TypeScript проверяет возвращаемые типы
const name = userGetters.getName(); // строка
const age = userGetters.getAge(); // число
Импорт типов
Импорт типов из других файлов
// @ts-check
// Импорт типов из TypeScript‑файлов
/** @typedef {import('./types').User} User */
// Импорт типов из node_modules
/** @typedef {import('express').Request} ExpressRequest */
// Импорт с переименованием
/** @typedef {import('./api').default as ApiClient} ApiClient */
Создание файлов объявлений
Создание файла types.d.ts в проекте:
// types.d.ts
declare module 'my-module' {
export interface Config {
apiKey: string;
timeout?: number;
retries?: number;
}
export function initialize(config: Config): void;
export function fetchData<T = any>(url: string): Promise<T>;
}
Затем используем его в JavaScript‑файлах:
// @ts-check
/** @type {import('my-module').Config} */
const config = {
apiKey: '12345',
timeout: 5000
};
// TypeScript обеспечит автодополнение и проверку типов
import { initialize } from 'my-module';
initialize(config);
Лучшие практики
При работе с JSDoc и TypeScript соблюдайте следующие рекомендации:
- Включайте
// @ts-checkв начале файлов, где требуется проверка типов. - Используйте
@typedefдля сложных типов, которые применяются в нескольких местах. - Документируйте все параметры функций и возвращаемые значения.
- Применяйте
@templateдля обобщённых (generic) функций и типов. - Создавайте файлы объявлений (
.d.ts) для сторонних библиотек без типов. - Используйте
@ts-expect-errorвместо@ts-ignore, если ожидаете ошибку.
Типичные ошибки
Обратите внимание на следующие распространённые проблемы:
- Отсутствие
// @ts‑check: Без этой директивы проверка типов работать не будет. - Неправильный синтаксис JSDoc: Даже одна опечатка может отключить проверку типов.
- Конфликты типов: Возникают, когда типы из разных источников не согласуются между собой.
- Проблемы с выводом типов: Иногда TypeScript не может корректно вывести типы автоматически.
- Производительность: Проверка больших JavaScript‑файлов со сложными типами может занимать значительное время.
Заключение
Использование JSDoc вместе с TypeScript — мощный способ обеспечить типобезопасность в JavaScript‑проектах без необходимости конвертировать файлы в .ts.
Этот подход особенно полезен в следующих случаях:
- постепенная миграция кодовых баз на TypeScript;
- добавление проверки типов в уже существующие JavaScript‑проекты;
- работа в средах, где файлы
.tsне поддерживаются; - документирование JavaScript‑кода с указанием информации о типах.
Следуя шаблонам и лучшим практикам, описанным в этом руководстве, вы сможете воспользоваться многими преимуществами TypeScript, продолжая работать с JavaScript.
Важно: Хотя JSDoc обеспечивает отличную проверку типов, для новых проектов или полной миграции рекомендуется использовать файлы .ts — так вы получите максимально полный опыт работы с TypeScript.
Готовы попробовать TypeScript с JSDoc?
Начните с добавления // @ts‑check в ваши JavaScript‑файлы, а затем постепенно добавляйте типовые аннотации с помощью JSDoc.
Компилятор TypeScript поможет вам обнаружить ошибки ещё до их попадания в рабочий проект!