Сделайте свое устаревшее приложение снова отличным…

Вступление

Почему ты бы так поступил ?

Работа разработчика редко сводится к работе только над передовыми технологиями и запуску каждые 2 месяца нового проекта с нуля (это то, чем вы занимаетесь? Тогда оставьте эту работу, это работа мечты каждого 😊). Большая часть работы разработчика заключается в сопровождении унаследованных приложений, потому что они по-прежнему приносят домой деньги, но, возможно, недостаточно, чтобы их переписывать каждые 5 лет. Иногда это старая технология, много времени жуткая вся кодовая база (я уже слышу, как вы кричите на авторов кода об этой дерьмовой реализации, даже если вы были автором 😁). Поэтому, когда вы возвращаетесь к старому проекту, хорошо бы потратить некоторое время на быстрые победы, которые помогут вам позже. Использование Typescript - одно из них. Здесь мы увидим, как выполнить эту миграцию.

Моя актуальная прикладная технология

  • angularJS 1.7
  • Несколько библиотек (на самом деле намного больше, чем вам нужно ...), например angular-ui-bootstrap, fullcalendar, d3js, jquery, underscore, moment.js и т. Д.
  • Система сборки grunt, которая транспилируется с помощью babel, а затем объединяет / искажает результат (без веб-пакета или просмотра)
  • Карма + жасмин для юнит-тестов

1. Настройте Webpack.

Машинопись поддерживает модуль, и это то, чего вам не нужно в своем приложении. Вот почему вам нужен веб-пакет. Вы можете выбрать использование browserify, но, на мой взгляд, Webpack намного производительнее, и вам не понадобится средство запуска задач.

Настроить конфиг

Вы хотите, чтобы веб-пакет работал, поэтому вам нужны 2 вещи:

  • Установить зависимости npm i -D webpack webpack-cli
  • Настройте конфиг webpack.config.js. К счастью, для этой части вам не нужно писать собственный файл с нуля. Вы найдете множество генераторов, таких как createapp.dev, которые сделают всю работу за вас. Просто выберите нужный вариант.

Импортировать все файлы js в корневой файл

Когда webpack заработает, вам нужно будет указать, какой файл является точкой входа. Проблема в том, что если вы настраиваете webpack, велика вероятность, что в вашем коде нет модулей ESModules и что у вас не будет ни одного файла записи. Решение состоит в том, чтобы создать index.js файл на корневом уровне вашего приложения, и вы вручную импортируете все файлы.

// index.js
import './feature1/feature1.controller.js
import './feature2/feature2.controller.js
// and so on for all your files in the right order...

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

Убедитесь, что приложение все еще работает

Если нет, исправьте все сейчас. Вероятно, у вас отсутствует импорт, или неправильный порядок, или что-то еще. Исправление приложения сейчас упростит следующую главу.

Исправьте модульные тесты

Это было моей самой большой проблемой, так как мы уже добавляем кучу тестов для части JS. Тесты тестировали некоторые глобальные функции, а также угловые модули. Поскольку теперь код был частично перенесен на импорт, тесты не смогли найти прежнюю глобальную функцию, поскольку они были включены в модули оболочки веб-пакета. Кажется (и я надеюсь, что я ошибаюсь, но не смог найти решение), что вам придется либо перенести все в модуль, либо также объявить некоторую функцию импорта как глобальную, чтобы тест прошел правильно.

import { otherDependencies } from ‘./otherDependency’;
export function helloWorld() { … }
window.helloWorld = helloWorld; // declare the function globally

Мы все согласны с тем, что это совсем не чисто, но это единственный способ начать интеграцию модуля без рефакторинга всего. После того, как тесты снова будут запущены и установка TS заработает, вы можете потратить время на рефакторинг до чистой базы кода импорта.

2. Настройте TypeScript.

Установить машинописный текст

npm install -D typescript

Добавить обработку ts файлов в webpack

module.exports = {
  ...
  module: {
    rules: [
      ...,
      {
        test: /\.tsx?$/,
        use: 'ts-loader',
        exclude: /node_modules/,
      }
    ]
 },
 resolve: {
   extensions: ['.tsx', '.ts'],
 },
};

Создайте tsconfig.json

TypeScript потребуется базовая конфигурация для компиляции так, как вы хотите. У вас будет вся необходимая информация в их документации: https://www.typescriptlang.org/docs/handbook/tsconfig-json.html

В моем случае я просто добавил эту простую конфигурацию в качестве первого шага.

{
  "compilerOptions": {
    "outDir": "./dist/",
    "noImplicitAny": false,
    "module": "es6",
    "target": "es5",
    "allowJs": true,
    "lib": [ "dom", "es7" ]
  }
}

noImplicitAny на данный момент имеет значение false. Не рекомендуется оставлять эту настройку отключенной, потому что вам не следует вводить код с any. Но пока мы не хотим менять код, так как вы можете внести новые ошибки. Мы просто хотим иметь работающую задачу компиляции, а ее уже достаточно. Позже следующим улучшением будет включение этих настроек и исправление всех неявных ошибок.

lib с es7 добавит функции, которые являются частью спецификации es7, к проверкам TypeScript. Например, у вас не будет ошибок при использовании string.includes, недоступного до es7. Я выбрал es7, так как уже использовал ESNext с babel в своем JS, но вы должны установить здесь значение, соответствующее вашему языковому уровню.

Добавить типы для глобальных объектов

Если вы попытаетесь запустить webpack для проверки компиляции, компилятор TS, вероятно, будет кричать на вас, потому что он не распознает глобальные переменные, такие как angular. В этом случае вам нужно будет научить TS, к каким типам относятся эти константы. Для большинства библиотек хорошие люди напечатали файлы *.d.ts, которые уже содержат все типы библиотеки. Просто установите их, чтобы TypeScript знал об их типах. В моем случае, чтобы сообщить об angular и jquery, я установил следующее:

npm i @types/angular @types/jquery

Никаких других настроек делать не нужно. По умолчанию TypeScript будет рассматривать все типы, доступные в папке: node_modules/@types, и именно там тип angular был установлен npm.

Исправить ошибки

На этом этапе вам нужно будет запустить компиляцию, чтобы увидеть, какие ошибки возникают. На данный момент мы просто хотим установить TypeScript в нашей кодовой базе, а не переписывать весь код, мы увидим, как мы можем быстро исправить типичные ошибки, которые могут возникнуть у вас. Это не лучшая практика и не окончательная реализация, вам придется очистить это в более позднем тикете. Но пока давайте просто скомпилируем эту кодовую базу.

Если у вас очень много ошибок по поводу любого типа, сначала убедитесь, что вы добавили "noImplicitAny": false в tsconfig.json, как описано выше.

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

Исправить объявления переменных

Некоторые переменные в коде были инициализированы как объект, а члены объекта были добавлены к объекту позже в коде.

var foo = {};
foo.bar = 'bar';
foo.baz = function(){ return 'baz'; }

Но TypeScript это не нравится, потому что вы сначала объявляете его как объект, а затем пытаетесь изменить этот тип, добавляя свойства. Решение состоит в том, чтобы реорганизовать этот код, чтобы использовать правильную инициализацию объекта. Как следующее:

var foo = {
  bar: 'bar',
  baz: function(){ return 'baz'; }
};

Исправить расширение области

В angularJs область действия компонента (или директивы) может быть расширена с помощью настраиваемых свойств, которые мы хотим использовать в компоненте. Но интерфейс IScope из delcaration углового типа знает только базовые свойства.

Чтобы исправить это, вам нужно будет создать собственный интерфейс, расширяющий IScope и содержащий ваши свойства. Бывший:

interface MyComponentScope extends angular.IScope {
  myProp1: number;
  // ... and so on for all your properties+types
}
// and then type the scope param like :
link: function (scope: MyComponentScope, ...) { ... }

Исправить привязки

При использовании компонентов в angularJS 1.5+ появляется новое свойство (несколько лет назад, да…), называемое bindings, которое привязывается к контроллеру. Как и расширение области действия, эти привязки являются настраиваемыми, и вам нужно будет определить их типы. Вот пример существующего объявления привязки:

class MyComponentCtrl {
    // existing members and methods here
    //...
}
angular.module('my-module')
.component('myComponent', {
    templateUrl: 'path/to/my-component.component.html',
    controller: MyComponentCtrl,
    bindings: {
        text: '@',
        resolve: '<',
        close: '&',
        dismiss: '&'
    }
});

Итак, теперь просто добавьте недостающие свойства привязки в класс

class MyComponentCtrl {
    // from the bindings
    text: string;
    resolve: { title: string, items: any };
    close: (obj: { $value: any}) => void;
    dismiss: () => void;
    // existing members and methods here
    //...
}

Исправьте модульные тесты

Вы уже сделали это во время настройки Webpack. Но поверьте мне, вы, вероятно, что-то сломали, поэтому снова запустите тесты и исправьте их.

3. Улучшить все

Теперь, когда установка, сборка и тестирование запущены, предстоит еще много работы, чтобы по-настоящему использовать всю мощь импорта, но оставшуюся часть можно выполнить вместе с другими новыми задачами:

  • преобразовать существующий код в класс es6 и импортировать
  • импортировать только используемые компоненты библиотек вместо полных библиотек
  • добавлять текст везде
  • Удалить все
  • Принудительно введите типизацию, ограничив использование типа any (конфигурации вроде noImplicitAny)
  • Удалите библиотеки, которые использовались только для функций, которые теперь являются частью ESNext (подчеркивание, jquery,…)
  • Вместо этого удалите глобальные объявления и импортируйте модули
  • Модульное тестирование модулей, а не приложения

Заключение

На переключение на Typescript у меня ушла 1 рабочая неделя. Вероятно, вы могли бы добиться этого за гораздо меньшее время, поскольку мне пришлось гуглить и много узнавать об инструментах, чтобы все работало правильно. Также я заработал кодовую базу за 2 дня до этого, так что это было не очень известное приложение, в котором вы можете ожидать сюрпризов.

В любом случае переход на Typescript без рефакторинга уже дал некоторые преимущества. Он уже обнаружил несколько ошибок, связанных с неправильными параметрами метода, неправильными вызовами функций,… и предотвратит появление новых ошибок (даже если мы знаем, что это небезопасно).

Надеюсь, эта статья вам тоже поможет.