Typescript — история адаптации к TFS

Как вы знаете, в решении TFS уже довольно давно есть пользовательский веб-интерфейс. В выпуске TFS 2012 он был изменен, причем достаточно кардинально — как внешний вид, так и архитектура. Мы перешли на REST/Json. Теперь интерфейс в большей степени полагается на выполняемые браузером Javascript-сценарии, и в меньшей — на Ajax-вызовы. В результате он начал работать быстрее и стал более удобным для использования. Однако и Javascript-сценариев стало намного больше! На сегодняшний день интерфейс содержит примерно 80 000 строк кода на Javascript (без учета пустых строк, коммента��иев и тому подобного — с ними объем кода достигает 150 000 строк). Это очень много! Написать крупное приложение на нетипизованном языке с поздним связыванием довольно непросто.

Пару месяцев назад, еще до того как заявить об этом, мы решили преобразовать весь код Javascript в Typescript. Несколько недель назад мы наконец попробовали это сделать, и сейчас я попытаюсь оценить преимущества такого решения. Первое, что бросилось в глаза, — количество сообщений об ошибках исходного кода, сгенерированных компилятором Typescript в ходе преобразования. Если проект крупный, то довольно сложно получить правильный код Javascript и проверить его. Typescript облегчает написание «корректного» Javascript, обнаруживая ошибки в большом объеме кода. Разумеется, такие ошибки были...

Стоит пояснить одну вещь. Мы преобразовывали нормальный код, то есть не запутанный или неаккуратно написанный. Этот код Javascript подвергался серьезным проверкам –– как модулей, так и функциональности. JSLint, инструмент проверки качества Javascript, не находил ошибок. Мы добросовестно работали над этим кодом и очень старались сделать его качественным. И тем не менее при конвертации обнаружилось 13 ошибок. Привожу список типов ошибок, которые находит Typescript.

  • Ошибка 987091: TFS.TestManagement.Controls.debug.js –– некорректный вызов результатов регулярного выражения при отсутствии совпадений.
  • Ошибка 986991: в вызове baseConstructor в нескольких фрагментах кода отсутствует параметр «this».
  • Ошибка 986997: обращение к неопределенному свойству ActionManager перемещает обработку свойства в начало, а не в конец –– TFS.Agile.debug.js.
  • Ошибка 987103: обращение к свойству неопределенного расположения в TFS.WorkItemTracking.debug.js; необходимо указать path().
  • Ошибка 987016: несвязанные или несуществующие функции в TFS.TestManagement.Controls.debug.js; возможно, возникла в результате некорректного рефакторинга.
  • Ошибка 987071: RichContentTooltip _setPosition, вызов несуществующей функции; вероятнее всего, опечатка в регистре.
  • Ошибка 987080: TFS.UI.Controls.Grids.debug.js _onToggle вызывает this._addSelection с несоответствующими параметрами.
  • Ошибка 987086: _beginCopySelection в TFS.UI.Controls.Grids.debug.js ссылается на несуществующее свойство, определяя, есть ли выделение.
  • Ошибка 986993: отсутствует параметр «this» при вызове базового конструктора в TFS.Agile.Boards.debug.js.
  • Ошибка 987104: при вызове Diag.assert * передается только один параметр, тогда как необходимо два
.
  • Ошибка 986987: отсутствуют значения строковых ресурсов для диалогового окна Area Iterations.
  • Ошибка 987054: некорректное обращение к ресурсу в TFS.Build.debug.js.
  • Ошибка 987101: опечатки в списках ресурсов в TFS.WorkItemTracking.Controls.Query.debug.js.

Наш подход...

Перед тем как приступить к миграции, мы обсудили наши планы с другими группами Microsoft, которые работали с Typescript ранее. Среди них была группа Эриха Гаммы (Erich Gamma). По оценкам Эриха, полноценное последовательное преобразование кода в комментированный Typescript можно выполнить со скоростью примерно 300 строк кода в час. Конечно, корректный Javascript-код автоматически превращается в корректный Typescript-код, поэтому достаточно сменить расширение файлов и перейти к компиляции. Но мы хотели воспользоваться преимуществами Typescript, для чего необходимо было аннотировать типы, а это делается вручную. 80 000 строк при скорости обработки в 300 строк в час — слишком много. Поэтому мы решили облегчить задачу и создать специализированный инструмент. К счастью, мы хорошо поработали над исходным Javascript-кодом. API были достаточно подробно описаны средствами Javadoc. Мы использовали согласованные шаблоны классов и модулей.

В итоге, мы (один разработчик) меньше чем за неделю создали инструмент (конечно, на Typescript :)), анализирующий Javadoc и прочие шаблоны и конвертирующий их в соответствующие конструкции на Typescript. Еще около недели ушло на работу утилиты, редактирование комментариев Javadoc (добавление недостающих частей и пр.), обновление построения, проверку конвертированного кода и все остальное. Разумеется, Typescript способен на большее. Например, у нас не было единого шаблона, который позволил бы распознавать интерфейсные контракты, и инструмент не мог конвертировать их в соответствующие конструкции на Typescript. Мы будем постепенно вносить изменения вручную, когда возникнет необходимость изменять модули, а значит, и улучшать Typescript. И скорее всего, мы обнаружим новые проблемные места, о наличии которых еще не подозреваем.

В целом мы более чем довольны результатом. Теперь нам доступны новые, облегчающие работу инструменты, такие как Intellisense, а код стал более структурированным и простым в обслуживании. Он улучшился! Это была хорошая и полезная работа. Если у вас имеется большой объем кода на Javascript, возможно, стоит сделать подобное преобразование. Количество ошибок даже в тщательно проверенном коде может вас неприятно удивить.