JavaScript to TypeScript - translation difficulties

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

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

Global variables are evil


If you have already decided to stick to OOP, discard the global variables AT ALL. JavaScript sometimes uses the “use strict” directive;

In TypeScrip, simply do not declare external variables with declare var. Make declarations only inside classes, at least so MyVar: any.

Probably everyone can tell a story why global variables are evil. I will tell mine. It can be said by mistake that I declared one variable through declare var xmlhttp. Well then, it seemed to me that nothing was known about her as a TypeScript class, which means it’s a candidate for an external variable. The TypeScript generator ignored this declaration when translating into 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, then later, of course, this leads to 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 a JS syntax error.

Inheritance script order


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

/// 
module CyberRise
{
    export class Menu extends RequestData
    {
    }
}


The Menu class has been declared that inherits from RequestData in the CyberRise module. In JS, this is generated by a furious construction, which I will not describe here. In fact, there is a three-level investment of functions (and even more), the purpose of which is to distinguish between visibility areas. Using modules we can not worry about the same class names in different libraries. But indeed, every developer likes to use a 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 correspondence of types and even at the stage of compilation (JS generation) it needs to know everything about types. Again, the advantages of strongly typed languages, I think everyone knows, I will note the most direct - error checking associated with the improper use of objects at the compilation stage. This greatly saves debugging time in large projects.

So, whoever is familiar with C and the reference construct, although it is formally under the comment, is analogous to include. And most importantly, the code will stop working if you connect scripts in the wrong order. In this case, it 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 include also to be in a certain order. Only since the declaration of scripts is not done in TypeScript, he cannot verify this at the compilation stage and you can spend a long time in prostration without understanding why your code is not running. Although there is an option when all the code is compiled into a single js file, then the TS compiler guarantees the correct arrangement of the code order itself. But for a serious project, this may not create convenience, because it’s more convenient to see the JS code file-by-file. Adhere to the style of "one file - one class."

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

Kalbeki, delegates and other synonyms


With assignment of a reference to a function, i.e. With the creation of a callback, there are also misunderstandings. But they are more likely to discipline, but JS followers may not seem natural. Those who are used to JS often, I think, are involved in passing links to the function, without even thinking about the safety of this. For example, in .NET intentionally forbidden direct work with links, because this often leads to bugs. But with OOP, this is often not necessary, object references are passed instead, and then the receiving object uses the public part of the class, using the members of the class it needs. Even better, if the class implements an interface, and a link to the interfaces is passed. Alas, this is not in JS, and therefore often use the unsafe transfer of references to the function.

So, the code of the form:

        xmlhttp.onreadystatechange = this.OnDataProcessing;


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

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


The thing is, now at the class level you can no longer declare local variables with var. Now all you declare at the class level = these are properties used through this. Therefore, if earlier they had turned a blind eye to such a crutch, now it looks even more unnatural.

Therefore, it would be better to use anonymous methods to eliminate the haunting shadow of objectless JS

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


External calls


Often we already use the 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 probably also say that the Window class has already been declared. But this will not be the class and not from the library that we mean. But if we used modules, then everything will be fine. And for the compiler to understand what kind of class it is, we need to describe the signature, similar to how we do it using methods from dll.

By writing:

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


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

Motivation


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

1. Behind all attempts to divide the data into modules, classes, objects, the presence of inheritance - there is a simple desire to differentiate the areas of responsibility of properties and methods, and to delimit the scope of variables deeper. To do this, TS is reversed as soon as it can, using the only opportunity for this in JS using closures to encapsulate data. So he introduces the concept of a class - as functions in a function, where one function is static data / methods / constructors at the class level, and object data in a nested function.
2. We get strict typing
3. We get such a bonus as full support for constructors in classes. Without them, the so-called factories with init () methods
4. Inheritance becomes not the implementation of aggregation, as it is by default in JS, but a real inheritance, with control over the task of the necessary constructors in the heirs. How is inheritance better than aggregation? Yes, it’s not better, but it is often necessary when you develop and override the behavior of a number of base classes. And then semantically this relationship is the kind of something (is a), and not 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 one more bonus - interfaces, as a 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 this, others do not. I can only say one thing: with the help of interfaces, multiple inheritance is implemented, and it is easy to divide the complex public part of the entire class into a number of interconnected interfaces, and then pass links not to the whole class, but links to the selected interface.
6. another trifle - there are enum`s
7. And it’s also nice that there is a so-called optional parameters i.e. those that do not need to be transferred functions, and more importantly, those that can not be implemented in the interface. This is sometimes even better than in C # - where you need to get rid of implementations of the return null type (to which my boss once joked that if he knew how to do such implementations in a commercial sense, returning as an implementation [something unreadable here], then it customers would be especially happy :)).

Perhaps that’s all, if you evaluate after a while I’ll write about the difficulties of translating databases - migration from MySQL to MS SQL Server.

upd.

People, explain to some below who speaks with a large aplomb (especially while expressing their misconceptions), how the interface differs from this
You will get the same thing by simply marking class members as public \ private


and I’m not interested in breeding a similar holivar / trolling, there are questions - write in a personal answer.

In this case, declare declares nothing, but tells the compiler that at run time there will be such a variable with a type so that it does not swear at its use.


So what? The article clearly states that this can lead to the use of a global variable. It is unclear how - re-read the article.

Also popular now: