Перейти к основному содержимому

TypeScript в проектах на JavaScript (JSDoc)

Комментарии JSDoc позволяют добавить проверку типов TypeScript в обычные файлы JavaScript, не переименовывая их в .ts.

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

Примечание: примеры на этой странице написаны как JavaScript с типами JSDoc. Сохраните их как файлы .js с // @ts-check в начале (или с включённым checkJs), чтобы увидеть описанную проверку типов. В файле .ts компилятор игнорирует типы JSDoc и опирается на обычные аннотации.


Начало работы

Чтобы TypeScript проверял ваши файлы JavaScript, нужно:

  1. Создать файл tsconfig.json (если у вас его ещё нет)
  2. Включить в нём checkJs или поставить // @ts-check в начале тех файлов, которые вы хотите проверять

Пример: JSDoc для безопасности типов

Комментарий JSDoc над функцией сообщает TypeScript, что оба параметра и возвращаемое значение являются числами.

Prompt
Покажите самую маленькую функцию с типами JSDoc и включённым // @ts-check в начале файла. Напишите функцию add(a, b), задокументированную с помощью @param {number} a, @param {number} b и @returns {number}, которая возвращает сумму. Нужно только объявление — программа ничего не выводит.
Вывод: программа на TypeScript и захваченный вывод

Объекты и интерфейсы

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

Встроенные типы объектов

Тип параметра записан прямо на месте как тип объектного литерала. В проверяемом файле .js вызов greet без lastName отмечается как ошибка.

Prompt
Покажите встроенный тип объекта, записанный в JSDoc для параметра функции. С включённым // @ts-check задокументируйте greet(person) с помощью @param {{ firstName: string, lastName: string, age?: number }} person и верните Привет, <firstName> <lastName>. Вызовите её один раз с полным объектом (John Doe) и один раз только с firstName: "Jane", пометив второй вызов комментарием об ошибке отсутствующего lastName, о которой сообщает проверяемый файл .js. Ничего не выводится.
Вывод: программа на TypeScript и захваченный вывод

Использование @typedef для сложных типов

@typedef вместе со строками @property определяет именованный тип User. Квадратные скобки помечают необязательное свойство, а объединение в круглых скобках ограничивает role несколькими значениями.

Prompt
Покажите теги @typedef и @property, описывающие тип объекта в JSDoc. Определите typedef User с id: number, username: string, необязательным email, полем role, ограниченным значениями "admin" | "user" | "guest", и методом getFullName, возвращающим строку. Объявите currentUser с /** @type {User} */ (id 1, username johndoe, role admin, getFullName() возвращает John Doe) и выведите currentUser.role.
Вывод: программа на TypeScript и захваченный вывод

Расширение типов

Пересечение (Point & { z: number }) строит новый тип на основе существующего. В проверяемом файле .js объект без z — это ошибка, которую и ожидает комментарий @ts-expect-error. При компиляции как файла .ts типы JSDoc игнорируются, поэтому компилятор вместо этого сообщает, что директива не использована.

Prompt
Покажите, как один typedef из JSDoc может расширять другой с помощью типа-пересечения. Определите Point как { x: number, y: number }, а Point3D как Point & { z: number }. Объявите point3d с типом Point3D и значениями x 1, y 2, z 3, затем объект point2d без z, перед которым стоит комментарий // @ts-expect-error - отсутствует свойство z. В файле .js с // @ts-check отсутствующее z — это ожидаемая ошибка; в файле .ts типы JSDoc игнорируются, поэтому компилятор вместо этого сообщает, что директива не использована.
Вывод: программа на TypeScript и захваченный вывод

Типы функций

JSDoc может описать каждую часть функции: её параметры, возвращаемое значение, типы колбэков и даже несколько перегруженных сигнатур.

Объявления функций

Каждый @param задаёт параметру тип и короткое описание; @returns делает то же самое для результата.

Prompt
Покажите полностью задокументированное объявление функции с типами параметров и возвращаемого значения в JSDoc. С включённым // @ts-check напишите calculateArea(width, height), задокументированную с помощью @param {number} для обоих параметров (с короткими описаниями) и @returns {number} Вычисленная площадь, возвращающую произведение. Сохраните calculateArea(10, 20) в area. Ничего не выводится.
Вывод: программа на TypeScript и захваченный вывод

Функциональные выражения и колбэки

@callback определяет переиспользуемый тип функции. Затем его можно использовать с @type для стрелочной функции или как тип параметра.

Prompt
Покажите тег @callback для типизации функциональных выражений и параметров-колбэков в JSDoc. Определите тип колбэка StringProcessor, принимающий string и возвращающий string. Объявите toUpperCase с @type {StringProcessor} как стрелочную функцию, затем функцию processStrings(strings, processor), задокументированную с помощью @param {string[]}, @param {StringProcessor} и @returns {string[]}, которая применяет обработчик к массиву через map. Вызовите её с ["привет", "мир"] и toUpperCase. Ничего не выводится.
Вывод: программа на TypeScript и захваченный вывод

Перегрузки функций

Несколько блоков @overload дают одной функции больше одной сигнатуры. Последний блок описывает реализацию, которая обрабатывает их все.

Prompt
Покажите теги @overload в JSDoc, которые дают одной функции две типизированные сигнатуры. Напишите add(a, b) с двумя блоками @overload — строка плюс строка возвращает строку, число плюс число возвращает число — а затем сигнатуру реализации с string | number. Если хотя бы один аргумент является строкой, верните конкатенацию через String(), иначе — числовую сумму. Вызовите её как add("Привет, ", "Мир!") и add(10, 20). Ничего не выводится.
Вывод: программа на TypeScript и захваченный вывод

Продвинутые типы

Более продвинутые возможности TypeScript — объединения, пересечения, дженерики, отображаемые и условные типы — тоже доступны через JSDoc.

Типы-объединения и типы-пересечения

& объединяет типы, а | разрешает один из нескольких типов. Проверка in сужает объединение внутри функции.

Prompt
Покажите типы-объединения и типы-пересечения, построенные с помощью @typedef в JSDoc. Определите Person (name, age), Employee как Person & { employeeId: string } и Visitor как Person | { guestId: string, visitDate: Date }. Объявите employee (Alice, 30, E123) и guest (G456, new Date()), затем функцию getVisitorId(visitor), которая с помощью проверки "guestId" in visitor возвращает либо guestId, либо name. Ничего не выводится.
Вывод: программа на TypeScript и захваченный вывод

Отображаемые и условные типы

@template делает typedef обобщённым. Тело typedef может затем использовать отображаемые типы, переименование ключей и условные типы точно так же, как файл .ts.

Prompt
Покажите, что typedef с @template в JSDoc может выражать отображаемые и условные типы. Определите обобщённый typedef MethodNames<T>, который оставляет только те ключи T, значения которых являются функциями, и обобщённый отображаемый тип Getters<T>, который переименовывает каждый ключ K в `get${Capitalize<K>}` с типом () => T[K]. Объявите userGetters с типом Getters<{ name: string, age: number }>, где getName возвращает "John", а getAge возвращает 30, и прочитайте оба значения в name и age. Добавьте export {}, чтобы name не конфликтовало с глобальной переменной. Ничего не выводится.
Вывод: программа на TypeScript и захваченный вывод

Импорт типов

Тип JSDoc можно импортировать из другого файла или из пакета с помощью import('...') внутри @typedef. Для библиотек, которые не поставляются с типами, вы можете написать собственный файл объявлений (.d.ts).

Импорт типов в JSDoc

Три способа перенести тип в файл JavaScript: из локального файла TypeScript, из пакета в node_modules и под новым именем.

script.ts
// @ts-check

// Importing types from TypeScript files
/** @typedef {import('./types').User} User */

// Importing types from node_modules
/** @typedef {import('express').Request} ExpressRequest */

// Importing with renaming
/** @typedef {import('./api').default as ApiClient} ApiClient */

Файл объявлений для модуля

Файл types.d.ts, который объявляет пакет my-module: интерфейс Config и две экспортируемые функции.

script.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>;
}

Лучшие практики

Следуйте этим практикам, когда используете JSDoc вместе с TypeScript:

  • Ставьте // @ts-check в начале каждого файла, который хотите проверять
  • Используйте @typedef для сложных типов, которые встречаются больше чем в одном месте
  • Документируйте все параметры функций и возвращаемые типы
  • Используйте @template для обобщённых функций и типов
  • Пишите файлы объявлений (.d.ts) для сторонних библиотек без типов
  • Предпочитайте @ts-expect-error, а не @ts-ignore, когда вы ожидаете ошибку

Частые ошибки

Обратите внимание на эти распространённые проблемы:

  • Отсутствие // @ts-check: без него (или без checkJs) ничего не проверяется
  • Неверный синтаксис JSDoc: одна опечатка может отключить проверку типов для этого комментария
  • Конфликты типов: типы из разных источников могут не совпадать
  • Проблемы вывода типов: иногда TypeScript не может определить тип самостоятельно
  • Производительность: большие файлы JavaScript со сложными типами могут проверяться медленно

Заключение

JSDoc вместе с TypeScript — это мощный способ добавить безопасность типов в проект на JavaScript, не переводя файлы в TypeScript.

Это особенно полезно для:

  • Постепенного переноса кодовой базы JavaScript на TypeScript
  • Добавления проверки типов в существующий проект на JavaScript
  • Работы в средах, где файлы .ts не поддерживаются
  • Документирования кода JavaScript информацией о типах

С помощью приёмов с этой страницы вы получаете многие преимущества TypeScript, продолжая писать на JavaScript.

Помните: для новых проектов или полного переноса файлы .ts по-прежнему дают лучший опыт работы с TypeScript.

Готовы начать? Добавьте // @ts-check в свои файлы JavaScript и начните добавлять аннотации типов JSDoc. Компилятор поймает ошибки до того, как они попадут в продакшн.