Направете наследеното си приложение страхотно отново...

Въведение

Защо направи това ?

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

Моята действителна технология на приложение

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

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

Typescript поддържа модул и това е нещо, което не искате в приложението си. Ето защо имате нужда от webpack. Можете да изберете да използвате browserify, но по мое мнение Webpack е много по-производителен и няма да имате нужда от taskrunner настрана.

Настройте конфигурацията

Искате 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 частта. Тестовете тестват някои глобални функции, а също и ъглови модули. Тъй като сега кодът беше частично мигриран към импортиране, тестовете не успяха да намерят предишна глобална функция, тъй като те бяха обхванати в модулите на обвивката на webpack. Изглежда (и се надявам, че греша, но не успях да намеря решение), че ще трябва или да мигрирате всичко към модул, или също така да декларирате някаква функция за импортиране като глобална, за да позволите на теста да работи правилно.

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

Изобщо не е чисто, всички сме съгласни, но това е единственият начин да започна интеграцията на модула, без да преработвам всичко. След като тестовете започнат отново и настройката на TS работи, можете да отделите време, за да направите това преработване до чиста кодова база за импортиране.

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

Инсталирайте 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 от декарирането на ъглов тип знае само основните свойства.

За да коригирате това, ще трябва да създадете персонализиран интерфейс, който разширява 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+, има ново (беше ново преди няколко години, да…)свойство, наречено свързване, което се свързва с контролера. Подобно на разширението на обхвата, тези обвързвания са персонализирани и ще трябва да дефинирате техните типове. Ето пример за съществуваща обвързваща декларация:

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 и импортиране
  • импортирайте използвани компоненти на libs само вместо пълни libs
  • добавете въвеждане навсякъде
  • Премахнете всички
  • Принудително въвеждане чрез ограничаване на използването на типа any (конфигурации като noImplicitAny)
  • Премахнете библиотеки, които просто са били използвани за функции, които сега са част от ESNext (долна черта, jquery,…)
  • Премахнете глобалните декларации и вместо това импортирайте модули
  • Единичен тест на модулите, а не на приложението

Заключение

Отне ми 1 работна седмица, за да направя това преминаване към Typescript. Вероятно бихте могли да го постигнете за много по-малко време, тъй като трябваше да търся в Google и да науча много за инструментите, за да накарате нещата да работят правилно. Също така спечелих кодовата база преди 2 дни, така че не беше добре известно приложение, където можете да очаквате изненади.

Във всеки случай преминаването към Typescript без рефакторинг вече имаше някои предимства. Той вече откри няколко бъга за грешни параметри на метода, грешни извиквания на функции,... и ще предотврати следващ (дори ако знаем, че не е брониран).

Надяваме се, че тази статия може да помогне и на вас.