📜 ⬆️ ⬇️

Javascript to typeScript - translation difficulties

Probably many are aware that JS has a fairly limited OOP implementation. Some people are satisfied with the level of OOP in JS, others do not see the need to adhere to the rules of OOP, others without OOP cannot write code. Here we will try without holivar to understand some of the transitions from JS to TS.

We will talk about the motivation for the transition in the conclusion of the article and more likely for those who understand the importance of the quality of the code. But let's say a couple of words at the beginning. When you make a small test code with an unclear commercial status, it is unlikely you'll lick this code. And OOP is a good way to code a code, it doesn’t affect the functionality of your code, on the contrary, it often delays fast writing of the features that you decide to make. Sometimes even performance suffers. But probably everyone knows that level when it’s already difficult for him to figure out his code, then you start to look at it and think about refactoring from time to time. If your language is interpretable, without strict typing and does not support OOP well enough, then you will delay this moment for a long time - but I still think about this. If your JS language is a good option to translate it to TS, you surely don’t lose anything. But there are some difficulties, due to which you may doubt the correctness of this decision during the translation process.

Global Variables - Evil


If you have already decided to adhere to the PLO - discard global variables AT ALL. JavaScript sometimes uses the “use strict” directive for this;

In TypeScrip, simply do not declare external variables with declare var. Make ads only inside classes, at least in the following way: MyVar: any.
')
Probably everyone can tell a story, why global variables are evil. I will tell mine. You can tell by mistake I declared one variable through declare var xmlhttp. Well, then it seemed to me that about it as a class TypeScript knows nothing, which means it is a candidate for an external variable. The TypeScript generator was ignored when translating to JavaScript, and so it became a global variable. Well, since this variable contained a link to XMLHttpRequest, which provides asynchronous data retrieval from the server, later, of course, this causes a bug as soon as you simultaneously receive different data types through this variable. Moreover, some browsers will level this, the code will even be able to work, but it will slow down significantly and failures will occur due to the JS syntax error. You do not always check in the code before using a variable, but is it set?

Order of scripts during inheritance


TypeScript supports true inheritance, how it differs from JS inheritance will be discussed later. It is declared like this:

/// <reference path="RequestData.ts" /> module CyberRise { export class Menu extends RequestData { } } 


The Menu class inherited from RequestData in the CyberRise module is declared. In JS, this is generated by a furious construction, which I will not describe here. In essence, there is a three-level embedding of functions (and even more), the purpose of which is to delimit scopes. Using modules, we can not worry about the same class names in different libraries. But indeed, every developer likes to use the common name, even such as Window.

You may also notice the reference construct at the beginning of the file. The fact is that TypeScript checks the type conformity and it is still at the compilation stage (JS generation) you need to know everything about types. Again, the advantages of strictly typed languages ​​I think everyone knows, I will note the most direct - error checking associated with the incorrect use of objects at the compilation stage. This greatly saves debugging time in fairly large projects.

So, whoever is familiar with C, the reference construction, although formally under a comment, is an analogue of include. And most importantly, the code will stop working if you connect scripts in the wrong order. This option does not work:

script type = "text / javascript" src = "js / Menu.js">
script type = "text / javascript" src = "js / RequestData.js">

and it works like this:

script type = "text / javascript" src = "js / RequestData.js">
script type = "text / javascript" src = "js / Menu.js">

Well, actually, this is similar to C ++, there should also include include in a certain order. Only since the declaration of scripts is not done in TypeScript, it cannot check this at the compilation stage and you can be in prostration for a long time without understanding why your code is not executed. Although there is an option when all the code is compiled into one js file, then the TS compiler guarantees the correct location of the code order itself. But for a serious project this may not create convenience, because It is still more convenient to see the code on the JS file. Stick to the style of "one file - one class."

upd. If you use the project HTML Application with TS Studio - then again this is not necessary, but I, for example, use the ASP.NET project - it does not know anything about TS, except that I tell it how to generate it in the post build. Therefore, I think if you have at least a less complicated project (and why else would you switch to TS?) Or you don’t use the project at all, you won’t use a project like HTML Application with TS.

Kalbeki, delegates and other synonyms


Assigning a reference to a function, i.e. With the creation of a callback, too, there are neponyatki. But they are more likely to discipline, but JS adepts may seem not natural. Those who are used to the JS often, I think, are engaged in the transfer of references to the function, without even thinking about the safety of this. For example, in .NET, they intentionally banned direct work with links, since This often leads to bugs. But with OOP, this is often not necessary, instead, references to objects are transmitted, and then the receiving object uses the public part of the class, using the class members it needs. Even better if the class implements the interface, and the reference to the interfaces is passed. Alas, this is not in JS, and therefore often use not secure transfer of references to the function.

So, the view code:

  xmlhttp.onreadystatechange = this.OnDataProcessing; 


Work for you will no longer be. It will be more accurate, but not in that context, i.e. need to use crutch js

  xmlhttp.onreadystatechange = this.OnDataProcessing.bind(this); 


The fact is that now at the class level you can no longer declare local variables with var. Now all that you declare at the class level = these are the properties used through this. Therefore, if earlier on such a crutch they closed their eyes, now it looks even more unnatural.

Therefore, it would be better to use anonymous methods to eliminate the haunting shadow of the non-object JS.

  this.xmlhttp.onreadystatechange = () => { alert(this.xmlhttp.responseText); } 


External calls


Often we already use those libraries that are developed in JS. And there they often suggest using already developed function objects. If they are called via new, like

  win = new Window({ className: "mac_os_x", title: locTitle, width: 1000, height: 600, destroyOnClose: true, recenterAuto: false }); 


the TypeScript compiler will tell you that it knows nothing about such a Window class (it then thinks it is a class :)). Indeed, we will surely say that the Window class has already been declared. But it will not be the class and not from the library that we mean. But if we used modules, then everything will be fine. And in order for the compiler to understand what kind of class we need to describe the signature, in the same way as we do it using methods from dll.

By writing:

  declare var Window: new (a: any) => any; 


the compiler will lag behind us, realizing that as a parameter we pass anything, and return anything. For good, you can register types more precisely.

Motivation


The motivation for such a transition from JS to TS can be different for everyone. And speaking of it, we risk halivar. Therefore, I will limit myself to the list that is important for me when I begin to structure the code and I need tools for this (even if many consider this as syntax sugar, but behind it is not only the syntax, but also the underlying implementation (or not the implementation) of the concepts in language):

1. For all attempts to divide the data into modules, classes, objects, the presence of inheritance - there is a simple desire to delimit the areas of responsibility of properties and methods, and to a deeper level delimit the areas of visibility of variables. To do this, TS turns out as soon as it can, using the only possibility for this in JS using closures to encapsulate data. So he introduces the concept of a class - as functions in functions, where one function is static data / methods / constructors at the class level, and in a nested function the data of the object.
2. We get strong typing.
3. We get such a bonus as full support for designers in classes. Without them, it is necessary to invent the so-called. factories with init () methods
4. Inheritance becomes not an implementation of aggregation, as it is by default in JS - but real inheritance, with control of specifying the necessary constructors in the heirs. How is inheritance better than aggregation? Yes, not better, but often necessary when you develop and block the behavior of a number of base classes. Yes, and then semantically this one-view is a kind of something (is a), and not a part of something (part of) - without separation of this semantics everything turns into one mess, where the relations of use, aggregation, inheritance become one and the same.
5. We get another bonus - interfaces, as the descriptive part of classes. It is sometimes difficult to explain their importance, and I will not try. Those who know how to use them will appreciate it, others will not. I can only say one thing. Multiple inheritance is implemented using interfaces, and a complex public interface is easily part of the whole class, divided into a number of related interfaces, and then passing links not to the whole class, but to the dedicated interface.
6. another trifle - there is enum
7. and it’s nice that there is a so-called optional parameters i.e. Those that do not need to transfer functions, and more importantly, those that in the interface can not be implemented. This is sometimes probably even better than in C # - where you need to get rid of implementations of the form return null (to which my boss once joked that if he knew how to do such implementations in a commercial sense, returning as an implementation [something not readable here], then customers would be especially happy :)).

Perhaps that's all, if you estimate through time, I will write about the difficulties of transferring databases - migration from MySQL to MS SQL Server.

upd.

People, explain to some below who speak with great aplomb (especially while expressing their own delusions) how the interface differs from this.
You will get the same thing just by marking the members of the class as public \ private


and I don’t care about a similar holivar / trolling, I have questions - write in a personal reply.

In this case, declare declares nothing, but indicates to the compiler that at runtime there will be such a variable with such a type that it does not swear at its use.


So what? The article clearly states that this may lead to the use of a global variable. It is not clear how - read the article.

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


All Articles