📜 ⬆️ ⬇️

The story of how we translated the project into almost a quarter of a million lines in TypeScript and survived

In 2016, statically typed javascript proved to be extremely popular. Many companies have taken advantage of these or other means to eliminate the drawbacks of the dynamic nature of JS. We were also attracted by the prospect of using the huge potential of static typing in our developments.


Choosing the tools, we first stopped on TypeScript and Flow. Although these projects are different, they are aimed at solving the same problem. Namely, allowing you to control data types, they provide more advanced, in comparison with pure JS, code organization, improve refactoring capabilities, allow programmers to work faster and more confidently.

Coherent Labs and TypeScript


We started looking at typed JS last year, when the project I'm working on now, a visual editor of user interfaces for HTML games, written mostly in JavaScript, has grown to more than 100 thousand lines of code. The editor is equipped with a lot of features, as a result, its code base has become more difficult to maintain and refactor. That is why we decided to do some research and find a solution for the problem we face.

After a while we came to two options - TypeScript and Flow. Both promised to give us static types for better control of the code, but that alone was not enough. We wanted to use the full power of ES6 and future versions of the language. Flow, in essence, is a static code analyzer, which means that we would have to use a transpiler for all our ES6 code, but using TypeScript, which is an add-on to JavaScript, we would get both static types and support for most of the latest ECMAScript features.
')
Given the above, it was easy to make a final decision. We settled on TypeScript.

About the popularity of statically typed javascript


Let's get a little distracted and talk about what static typing is and how it found its place in the dynamic world of JavaScript.

The main difference between statically typed and dynamically typed languages ​​is how they perform data type checking. The former do this at compile time, and the latter at runtime. In other words, statically typed languages ​​require the programmer to declare data types before using them, while in dynamically typed languages ​​this is not necessary.

JavaScript, being a dynamically typed language, allows different types of data to be used in various operations. For developers who came to JS from statically typed languages, like Java or C ++, this, in most cases, looks completely illogical and even strange. This leads to serious trouble in large-scale projects. For example, a programmer may need to spend many hours only to find out why suddenly some variable turned out to be NaN.

TypeScript and Flow were created to solve such problems. Flow, which has gained popularity in the past two years and is focused only on type checking, is supported by Facebook. TypeScript, on which Microsoft had a hand, on the other hand, is an add-in over JavaScript. This means that it not only supports type checking, but also allows you to work with the future capabilities of ES6 and ES7. Both, and the other, have compilers that convert the code into pure JavaScript, which can be run in any browser.

Here are some advantages of using typed javascript:


When deciding whether to develop projects using typed JavaScript, ask yourself the following questions:


If the majority of answers to these questions are positive, then you should consider the possibility of transferring the project to TypeScript or Flow.

The eternal battle of the titans: TypeScript and Flow


As already mentioned, both Flow and TypeScript give the JS developer a very important, and, in my opinion, very necessary opportunity - a type system.

TypeScript appeared in 2012, with the assistance of Microsoft, when Anders Hejlsberg released his first release. Among other things, it supports optional type annotations and features of ES6 and ES7. It has a compiler that processes annotations and generates code in regular JavaScript.

Flow is a static code analyzer that Facebook does. It is designed to quickly find errors in JavaScript applications. This is not a compiler, but an analyzer. It can work at all without any type annotations, perfectly revealing itself in the task of printing types of variables.

Take a look at a simple example. There is the following function, which, taking the name, returns the greeting:

function greet = function(name) {   return `Hello, ${name}!`; } 

And using TypeScript, and using Flow, this function will look something like this:

 function greet(name: string): string {  return `Hello, ${name}!`; } 

A “String” after a function parameter is a way to specify a type in TypeScript and Flow. Types can be both simple and composite. This means that if we try to use an argument whose type is different from the specified one, an error message will be displayed. Consider an example of using a function.

 const message = greet(43); 

We called the function by passing a number to it as an argument. When compiling code, TypeScript will display a message that an error has occurred:

 Argument of type 'number' is not assignable to parameter of type 'string.' 

Catching such errors during compilation can have a very positive impact on productivity and even on the mood of the developer. TypeScript also supports modern JS features such as switch functions, import and export, generators, async / await mechanisms, classes, and so on. In the end, it turns out that TypeScript allows you to create an excellent ecosystem of developing applications in JavaScript.

So now let me tell you how we translated a project containing more than 200 thousand lines of code into TypeScript.

Step one - the choice of technology


Our project was rapidly developing, over time the volume of the code base increased almost exponentially. In those days, TypeScript and statically typed JavaScript grew in popularity. In all this we saw an opportunity that we should not have missed. After some research and a couple of meetings, we stopped on two possible hikes:


We chose TypeScript for several reasons:


After six months of work, I can frankly say that choosing the TypeScript was the right decision. Not everything went smoothly during the project transfer process, but we didn’t face anything too complicated.

Step Two - Start


After a long discussion of the transition process, in which the whole team participated, we decided to start with a small module and rewrite it. The very beginning is the replacement of file extensions from .js to .ts.

Next was the configuration of the assembly tool (Grunt) using grunt-ts , and, after a day and a half, the first TypeScript module was ready. The main difficulty was working with global and external modules. TypeScript has strict rules regarding typing. As a result, the TypeScript compiler does not recognize helper libraries, like jQuery and Kendo UI, and their methods. This immediately gave more than 200 compilation errors, indicating that TypeScript could not find a variable of type “$”. The solution was quite simple - we used the declare instructions:

 declare var $; declare var kendo; ... 

The declare keyword is used to create so-called surrounding ads. They are used to provide TypeScript information about other libraries, data types, the source of which is not a TS file. In that situation, this turned out to be a perfectly acceptable solution.
At this stage, we successfully transferred the first module to TypeScript and were ready to go into work with the head, translating the entire project to TypeScript.

Step Three - Transition


The next step was to transfer all other modules to TypeScript. Some of them were strongly interconnected and it was necessary to think of a way to quickly make them workable. After some additional research and a couple of hours of brainstorming, we came up with a relatively simple strategy.

To begin with, we renamed all the files, giving them the .ts extension, and immediately ran into a squall of compilation errors. Then we added the necessary TypeScript declarations and assigned the data type Any some variables. This allows you to write to the variable data of various types. In this way, variables were declared that can store data of different types, say, strings, numbers, logical values.

After a two-day fight with types and declarations, we reached the first complete compilation of TypeScript. However, there was still a lot of work to do. It was necessary to improve the type checking, add the appropriate declarations for external libraries, and rework the ES5 code to ES6 standards.

The declaration file describes the library device. Using them (they are also called .d.ts files), you can avoid the misuse of libraries and get conveniences like autocompletion of commands in the code editor. When working with TypeScript 1.8, in order to add a new ad file, you need to install separate npm packages globally, find ad files for a specific library, and save all library description files in the “typings” folder. Honestly, the decision was not the most elegant. TypeScript 2.0 has a new way to manage library declarations — using the npm registry. This is all done now with a couple of commands. First, the installation:

 npm install --save @types/jquery 

Then - import where it is needed:

 import * as $ from 'jquery' 

As a result, there is no more boring mess with files and folders.
So, we dealt with external libraries. The next step was to change all ads of the type Any to the appropriate data types. This task took several days. We decided not to hurry and process the code step by step. In particular, they refactored under ES6 and changed types. After a couple of weeks, we transferred 80% of the code incrementally to ES6, providing it with meaningful type annotations. At this stage it was possible to move on to the next stage of work - testing.

Testing and TypeScript


Our development environment was close to ideal and we were ready for the next big stage of the project work - end-to-end (E2E) testing. We decided to use javascript tools. Our arsenal is Mocha, Chai, Selenium and WebDriver Selenium. We isolated the testing code — the Selenium framework, tests, and all dependencies, providing them with a separate tsconfig.json.

The presence of the tsconfig.json file in the directory indicates that the directory is the root of the TypeScript project. This file specifies the root files and compiler options.

We had to create one such file for the project itself and refactor the Grunt task in order to correctly use different configuration files. Now we were ready to start working with the Selenium framework.

The framework structure consists of seven core and five auxiliary modules. Here are the main modules:


Auxiliary modules are divided into categories of tests. So, we had a JavaScript execution module, a properties panel module, a timeline module module, and so on. They all have methods for performing specific tasks.

In addition, we added a report creation tool - mochawesome , which output a full report on all tests after they are completed.

Thanks to TypeScript, the project managed to use the capabilities of async / await , as a result, we came to cleaner and better organized code.

Future plans


In fact, what I told you is just the beginning. Switching to TypeScript is just a drop in the ocean. We still have a lot to do. Here are some of our plans:


In addition, we are experimenting with libraries that support working with a virtual DOM, like React and InfernoJS. Both that and another perfectly support TypeScript, the project can greatly benefit from the performance gains that work with the virtual DOM gives.
We are closely following the development of TypeScript. The latest releases introduce new features, such as type mapping, spread and rest operators for working with objects, supporting asynchronous functions when compiling in ES3 and ES5. Future releases are expected to support generators for ES3 / ES5, improved means of generalized programming, asynchronous iterators.

Results


The decision to use TypeScript in our project turned out to be correct, it brought a lot of good. In particular, it is an increase in labor productivity, improvement in code controllability, support for ES6. Initially, I was suspicious of TypeScript, but after working with him I can say that he is what I missed for a very long time.

Although I highly recommend trying TypeScript, experimenting, you, thinking about translating your own project to TypeScript, should take into account all the details. It is necessary to take into account the time required for processing the code base, the project features, the possible need for refactoring, and, most importantly, the opinion of the design team on TypeScript.

In summary, I want to say that TypeScript can certainly be considered a significant phenomenon, the impact of which on the JavaScript world will make it better.

Dear developers! And how do you feel about static typing in JavaScript? If you decide to recycle a large JS project, what would you choose?

Source: https://habr.com/ru/post/323612/


All Articles