DEV Community

Cover image for TypeScript From The Perspective Of A Java Programmer (I)
Meditator Gu
Meditator Gu

Posted on

1

TypeScript From The Perspective Of A Java Programmer (I)

A while ago, I used TypeScript to refactor a JavaScript project. But before that, I was a developer specializing in Java. In the field of business system development, I mainly study SpringBoot, MyBatis, Tomcat, microservice technology, databases, etc. I have also basically learned some big data-related technologies, such as Hadoop, HBase, and Hive, and developed a big data system for a year. For other languages ​​and related technologies, whether for development or for writing scripts, I have more or less dabbled in some of them. For example, writing bash scripts in Linux, writing Powershell scripts under Windows, using Python to build a simple server that provides algorithm functions, and using jQuery to write some simple web pages. Because of different usage scenarios and different problems faced, the related development modes, code writing methods, component ecology, and even the way programmers think about problems are very different.

Before I learned TypeScript systematically, I occasionally wrote some TypeScript code as an amateur front-end developer. I copied other people's code, modified it, and met some less complex requirements. Some of these copied codes were from netizens, some were official website examples, and some were already written by my colleagues. When I just graduated, I used WebStorm to write front-end code, but in recent years I have gradually changed to VSCode. Compared with the front-end, the development environment IDEA mainly used by back-end development has a higher degree of tool integration. If the project and tools are configured correctly, then developers actually do not need to open the command line, and many things can be done very well by clicking buttons. For example, developers can import dependencies, run code, compile jar packages, etc. by clicking menus and buttons. IDEA will execute related commands on behalf of developers. It is relatively convenient and fast. However, for the front-end, if you want to develop a project well or manage a project well, the understanding of the tool chain is much higher than that of the back-end. This is the first big difference between them.

When I decided to use TypeScript to develop some programs systematically, I first had to choose a suitable project creation tool. The competition for project creation tools in the front-end is more intense than that in the back-end. The back-end is basically Maven. Although there are some other competitors in this regard, such as Gradle. But the developers I know will choose Maven. On the back-end, you only need to click on the interface to create a project through IDEA and Spring Initializer. The front-end requires some commands to do these things. For me, the first is npm, then pnpm, and finally vite. After doing these, I still need to spend some time on some configuration files, such as tsconfig.json, vite.config.js, package.json, etc. Make sure that the project built by these files meets my expectations.

JavaScript's flexibility allows it to run not only in the browser, but also in node. Once it runs in node, it can be an http-server or a scripting language like bash. When I first started learning TypeScript, I was quite confused about this. The same JavaScript code will be subject to different constraints due to the different running environments. In the browser, I can use window to get global variables and use document to create DOM elements, but I can't do that in the node environment. Similarly, in the node environment, I can use the path library to read and write local files, but I can't do that in the browser. Even the debugging methods are quite different. If it is node code, then the debugging method is similar to Java. Just start the code in debug mode. But if it is for the browser, it is relatively troublesome. You need to configure the browser to open it in debug mode first, and then connect to the browser's remote debugging port through socket. Finally, you must ensure that the mapping of the execution code to the source code is correct.

After solving the problems of project creation and configuration, it is time to officially start writing code. Java and TypeScript are very similar in terms of code writing. They are both descendants of the C language family and are both object-oriented. However, the historical burden of TypeScript is much heavier than that of Java. TypeScript has to be as compatible with JavaScript as possible and take on the responsibility of object-oriented, which faces great challenges. The only historical burden of Java that I know is that it is a generic erasure. JavaScript itself is flexible to write, and it was quite practical in the past when the source code of several web pages on a website was independent of each other. But now I have to develop more complex code. What I developed is a library containing more than 100,000 lines of code. JavaScript is a bit overwhelmed. The lack of clear calling relationships, unclear parameter types, and lack of support for polymorphism are quite fatal. Unfortunately, as a front-end language, TypeScript will eventually be compiled into JavaScript, and it is still limited by some designs and defects of JavaScript.

When I tried to write code in an object-oriented way, the first limitation I encountered was that the interface in TypeScript would be erased after compilation. The interface in TypeScript is more like an upgraded version of JSDoc than a specific type. It is deeply involved in the code development process, providing developers with important type information, while also restricting developers from writing non-standard code, but it disappears quietly after compilation. Its quiet disappearance makes it impossible for me to use instanceof to determine whether an object implements this interface during the development process. This is a pity.

The status of the interface in the object-oriented code development process needs no further explanation. Many specifications emphasize the importance of interfaces. For example, the Dependency Inversion Principle in the six principles of design patterns emphasizes programming for interfaces rather than specific classes. By reading the definitions of the relevant methods in the interface, we can understand the capabilities of this interface at a glance without having to go back and forth to the specific implementation code. If the type declared for the variable is an interface rather than a specific class, it will be easy to replace or rewrite the class in the future. In the interaction between components, all APIs of a component are declared in the form of interfaces, and an independent module is extracted. The caller calls the interface through this interface module, and the implementer makes a specific implementation in another module. This is a very good way to decouple. In general, the interface in TypeScript does have some shortcomings due to the problem of interface erasure. However, it also provides most of the capabilities required by the interface feature. Interfaces in TypeScript support polymorphism, which surprised me because JavaScript does not support polymorphism. However, TypeScript interfaces can declare methods with the same name but different parameters. However, in the implementation process, there can only be one function to implement all interfaces with the same name.

Null and undefined in TypeScript is another confusing problem I encountered. If we consider the equality symbol "===" together, it will be even more confusing. In Java, there is only one kind of null, that is null. Although some reports claim that the problems caused by null pointers have caused a lot of loss of business value. In the actual development process, null pointers are indeed very common. But there has been no good way to deal with this problem. I have written some C# before. In C#, the type must be separately declared whether it is null or not, which surprised me at the time. But what surprised me more was that TypeScript not only has null, but also undefined. These things are mixed together, and it is indeed more difficult to deal with. Although there are some related switches in tsconfig that can turn off the detection of null or not distinguish between null and undefined, in fact, for higher code quality, I cannot turn them off. In the process of declaring a variable, both null and undefined must be reflected. This will make the code very lengthy. For example, the following example:

class Person{
  name: string | null | undefined
}
Enter fullscreen mode Exit fullscreen mode

In a simple name declaration, null and undefined occupy half of the space, but only provide a little useful information. If they appear in large numbers in the code, the proportion of code with business meaning will be much less. Mixing them will also cause some problems. I can give two examples that I have actually encountered.

The first example is when using the strict equality check, null !== undefined. That is, null and undefined are not strictly equal. If you want to clearly check that person.name is not null, you need to write more code and do more work. Write it like this:

if(person.name !== null && person.name !== undefined){
  ...
}
Enter fullscreen mode Exit fullscreen mode

This is actually unnecessary. Spending more time and energy on the matter of judging null will reduce the time and energy spent on specific function development. Tools serve developers, and developers need to understand tools, but they should not always deal with some special problems of tools.

The second example is a bug I encountered. After a long time of locating, I found that it was caused by the different truth and falsehood of null >= 0 and undefined >= 0. The value of null >= 0 is true, while the value of undefined >= 0 is false. If a variable x of type number can be null or undefined, then the writing of x >= 0 may cause some strange problems in some special scenarios.

In the TypeScript project I'm responsible for, I choose to use null extensively and use undefined cautiously. When necessary, I need to set the variable with the value of undefined to null in time. After all, the specific semantics and functions of undefined and null are different. You can't generalize them or simply use only one of them.

In addition to the two issues mentioned above, in the process of systematically learning and using TypeScript extensively, I also found that TypeScript has made a lot of efforts to make up for the shortcomings of JavaScript in object-oriented development. As a Java developer, I know the role of these syntaxes very well. For example, the return type of a function is never, assert crond, arg is number. JavaScript is not object-oriented at all. TypeScript wants to be more thorough in object-oriented. Therefore, we have to use something like an adapter to ease the contradiction between the two languages.

I will share my views on these issues in detail in subsequent blogs. Welcome to leave a message or send a private message to discuss with me.

Heroku

Deploy with ease. Manage efficiently. Scale faster.

Leave the infrastructure headaches to us, while you focus on pushing boundaries, realizing your vision, and making a lasting impression on your users.

Get Started

Top comments (0)

👋 Kindness is contagious

Explore a trove of insights in this engaging article, celebrated within our welcoming DEV Community. Developers from every background are invited to join and enhance our shared wisdom.

A genuine "thank you" can truly uplift someone’s day. Feel free to express your gratitude in the comments below!

On DEV, our collective exchange of knowledge lightens the road ahead and strengthens our community bonds. Found something valuable here? A small thank you to the author can make a big difference.

Okay