company_banner

Использование возможностей TypeScript в JavaScript без написания TypeScript-кода

Автор оригинала: Daniel Mackay
  • Перевод
Я — большой любитель TypeScript. По возможности я стараюсь использовать этот язык в своих проектах. TypeScript даёт разработчику отчёты об ошибках и проверку типов в JavaScript и TypeScript-коде. Кроме того, тот, кто пишет на TypeScript, может пользоваться удобными возможностями по автозавершению кода, может, быстро и надёжно, выполнять рефакторинг. TypeScript — это первая линия обороны от ошибок в коде. Ещё одна сильная сторона этого языка заключается в том, что он позволяет, при написании программ, пользоваться самыми свежими возможностями JavaScript. При этом то, что получается при преобразовании TypeScript-кода в JavaScript, будет поддерживаться всеми браузерами. Это очень приятно.



Правда, не все фронтенд-проекты предусматривают применение TypeScript. Что если можно было бы пользоваться возможностями TypeScript, но при этом не переводить на него весь проект (и всю команду), и не добавлять в конвейер сборки проекта новый инструмент? Это возможно благодаря VS Code и JSDoc.

Настройка рабочей среды


▍Вариант №1. Настройки VS Code (глобальная проверка)


Первый способ использования TypeScript (TS) в проектах, написанных на обычном JavaScript (JS), заключается в том, чтобы применять TS для проверки всех JS-файлов. Делается это посредством включения одного глобального параметра VS Code. Этот параметр можно включить в настройках пользователя или рабочего пространства:

"javascript.implicitProjectConfig.checkJs": true

Если вы из тех, кто предпочитает пользоваться графическим интерфейсом, то включить это можно так, как показано ниже.


Включение проверки JS-кода средствами TypeScript

▍Вариант №2. Использование файла jsconfig.json (глобальная проверка)


Ещё один вариант включения глобальной проверки JS-кода средствами TS заключается в использовании файла jsconfig.json. Если такой файл имеется, настройки, заданные в нём, переопределят настройки, заданные в VS Code.

{
  "compilerOptions": {
    "checkJs": true
  }
}

▍Вариант №3. Включение проверки для отдельных файлов


Третий способ использования TypeScript для контроля JS-кода предназначен для включения проверки на уровне отдельных файлов. Он заключается в добавлении в начало файла соответствующего комментария:

// @ts-check
let itsAsEasyAs = 'abc';
itsAsEasyAs = 123; // Error: Type '123' is not assignable to type 'string'

Используя ту же идею, можно выключить TS-проверку для отдельного JS-файла. Делается это в том случае, если TS-проверка включена глобально с использованием вышеописанных способов. Тут тоже используется специальный комментарий:

// @ts-nocheck
let easy = 'abc';
easy = 123; // ошибки нет

А если надо, чтобы TypeScript игнорировал бы лишь часть файла, можно поступить так:

let easy = 'abc';
// @ts-ignore
easy = 123; // ошибки нет

Типизация кода с использованием JSDoc


Только что мы поговорили о том, как включать TS-проверку на уровне файлов. Это даёт базовые возможности по проверке типов. Их можно расширить, описывая типы с помощью комментариев формата JSDoc.

▍Типизация функций


Начать типизацию кода средствами JSDoc можно с описания того, что функции принимают на вход:

/**
 * @param {number} shippingVal
 */
updateShipping(shippingVal) {
    ...
}

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


Подсказка по типу значения, принимаемого функцией

Этот метод хорошо подходит для простых типов, но что если разработчику нужно описывать собственные типы? Сделать это можно с помощью тега @typedef. Рекомендую помещать описания типов в начале файла. Это облегчит обнаружение таких описаний в ходе работы:

/**
* @typedef {Object} CreditNoteTaxResponseViewModel
* @property {number} feeAmount
* @property {number} inclGst
* @property {number} subTotal
* @property {number} total
*
* @typedef {Object} ApiResponse
* @property {string} status
* @property {string} message
* @property {CreditNoteTaxResponseViewModel} response
*/

Такие описания можно использовать там, где это нужно:

/**
                * @param {CreditNoteTaxRequestViewModel} req
                * @returns {Promise<ApiResponse>}
                */
                createCreditNoteTaxApiCall(req) {
        ...
                }

Ещё один вариант применения этой методики заключается в перемещении объявлений типов в особые файлы. Скажем, такой файл может называться main.d.ts.

export interface ICreditNoteTaxRequestViewModel{
    orderID: number;
    shippingCredit: number;
    lines: IICreditNoteTaxLineViewModel[]
}

export interface ICreditNoteTaxLineViewModel{
    originalOrderLineID:number;
    creditQuantity: number;
}

export interface ICreditNoteTaxResponseViewModel{
    feeAmount: number;
    inclGst: number;
    subTotal: number;
    total: number;
}

export interface IApiResponse{
    status: string;
    status: message;
    response: ICreditNoteTaxResponseViewModel;
}

Эти типы потом можно использовать в JavaScript:

/**
   * @param {import("./main").ICreditNoteTaxRequestViewModel} req
   * @returns {Promise<import("./main").IApiResponse>}
   */
  function createCreditNoteTaxApiCall(req) {
    /// некие действия
    return;
  }

▍Типизация обычного кода


Вышеприведённые примеры решают задачу типизацию входных и выходных значений функций. Нечто подобное можно делать и с помощью встроенных в код JSDoc-комментариев.


Типизация обычной переменной

▍Типизация библиотек


В VS Code есть система автоматического получения типов для сторонних библиотек. Соответствующая процедура применяется ко всем пакетам, описанным в файле package.json. Но, если кто-то предпочитает задавать подобное в явном виде, он может внести соответствующие настройки в jsconfig.json:

{
  "typeAcquisition": {
    "include": ["jquery"]
  }
}

После того, как система получения типов обработает библиотеку, типы можно будет использовать в JSDoc:

/**
 * @param {JQuery<HTMLElement>} $itemRow
 */
initRow($itemRow) {
    ...
}

Переход на TypeScript


Если вы решили перевести на TypeScript JavaScript-проект, в некоторых частях которого используется TypeScript, можно просто переименовать jsconfig.json в tsconfig.json и включить в нём параметр allowJs:

{
  "compilerOptions": {
    "allowJs": true,
    "checkJs": true
  }
}

После этого можно приступать к переименованию *.js-файлов в *.ts-файлы и к типизации кода этих файлов. Процесс перевода проекта на TypeScript может происходить постепенно.

Итоги


Прочтя этот материал, вы могли заметить то, как легко воспользоваться возможностями TypeScript в JavaScript-проекте. Для этого нужно лишь соответствующим образом настроить VS Code. Описанный здесь подход позволяет не вносить никаких изменений в процесс сборки проекта, не рисковать нарушением этого процесса, не принуждать команду разработчиков к срочному переходу на новый язык.

Если же JS-проект, в котором используются лишь некоторые возможности TypeScript, решено будет полностью перевести на TS, сделать это тоже несложно. К тому же, такой переход можно осуществлять поэтапно.

Вот GitHub-репозиторий, в котором можно найти примеры кода, использованные в этой статье.

Уважаемые читатели! Как вы относитесь к идее проверки типов в JS-коде с использованием возможностей TypeScript?

RUVDS.com
RUVDS – хостинг VDS/VPS серверов

Комментарии 4

    0
    Уже несколько месяцев пользуюсь при работе с легаси проектом. Все надеюсь, что исправлю все ошибки типизации и включу проверку с помощью tsc в билд.
    Кроме кучи ошибок, которые страшно трогать не смог побороть привязку интерфейса из .d.ts к классу. @implements не подхватывается.
    А вообще — отлично.

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

    Забыл сказать, что подсветка ошибок работает не только в VSCode но и в шторме, хотя немного разное поведение * и any
      0
      Тоже использую данный подход в вебшторме. Но там есть много нюансов: не все проверки корректно работают, не всегда выводятся верные типы и прочее. Использую именно такой подход вместо TS по причинам:
      1) TS компилируется во что-то другое, это «чёрный ящик» в итоге. Многие убеждают, что это не проблема, но у меня проблемы с этим бывают.
      2) TS работает только на классах с прототипным наследованием. Но в JS наследование может быть не прототипным, а например на основе функций-конструкторов, которые имеют много преимуществ: простые в реализации честные приватные переменные без # и приватные методы, исключается необходимость вечного this, нет путаницы в this во вложенных функциях (не стрелочных) потому что он единый для всего кода внутри конструктора (const t = this), возможность композиции объекта многими конструкторами сразу и прочее. Недостатки понятно тоже есть.
      3) Много времени тратится на верное разруливание типов вместо реальной задачи. Иногда может уйти день чтобы начитаться документации по TS и наконец сварганить мега-решение лишь для верной работы типов.
      4) Все d.ts, что я видел, имеют ошибки и неточности. Приходится либо ограничивать что-то, либо дописывать прямо в d.ts, чего быть по идее не должно. Почему-то писатели d.ts ограничиваются официальными документациями модулей, где описаны всегда не все случаи. Например в jQuery при выборке возвращается вовсе не HTMLElement, а Element. Потому что можно сделать выборку из SVGElement например.
      И много прочего, что сильно мешает. Получился холиварный комент, пардон. Имхо лучше бы в JS была типизация как когда-то в ActionScript3 — простая и ненавязчивая.
        0
        Типизация в JS закроет вход в программирование половине школьников.
          0
          Думаю что не обязательно. Потому что может быть не обязательной. Типа по умолчанию всё any как сейчас. Хотя наверное вы правы, так как будет везде код с типами в интернете.

      Только полноправные пользователи могут оставлять комментарии. Войдите, пожалуйста.

      Самое читаемое