Thinking in TypeScript: for the Eager Java Developer

Navpreet Gill
SSENSE-TECH
Published in
8 min readJan 28, 2022

According to the Stack Overflow Developer Survey for 2021, JavaScript has been the most popular programming language for the past nine years, with Java coming in fifth, barely ahead of JavaScript’s considerably more responsible cousin Typescript.

In my professional experience writing code and building backend solutions, Java has been my go-to language ever since I fell in love with it about 9 years ago. I never thought I would be working with another language in 2022, but we are living in a language-agnostic world that has evolved from its cocooned stage of technology, and where syntaxes are the least of our worries. TypeScript has provided me with enough safety to learn about the Wild West side of JavaScript.

Java & TypeScript @ SSENSE

At SSENSE, we employ a variety of languages, based on the use case or the problem being solved. However, we still have Java microservices, ready to be overhauled by other modern programming languages, and plunged into oblivion.

Coming from a strongly typed and object-oriented programming language like Java, you might not want to go all the way into a functional programming language like JavaScript, but TypeScript (TS) is an excellent alternative. The striking similarities between these two languages (TS and Java) also stem from the fact that the same person created both TypeScript and C#; Anders Hejlsberg.

JavaScript was created to serve as an intermediate layer between the application logic and the user interface for basic online interaction. Java, on the other hand, was designed from the start to be a full-fledged object-oriented programming language. Since then, both have evolved into the fantastic programming languages that we know and love. Other languages translate to JavaScript, but TypeScript is by far the most common. The complete list of languages that compile to JS can be found here.

It is quicker to set something up in JS/TS compared to Java. Java is a boilerplate-oriented language that emphasizes code verbosity. With TypeScript it is easy to simply set up your system and start working on the logic (read coding) that matters most while solving problems. This makes the TypeScript ecosystem more nimble and adaptable. You can make or break things faster, leading to greater agility; one could even argue that it’s easier to use in big projects.

My First Experience With TypeScript

The first TypeScript project that I ever worked on was a re-engineering/migration project of a legacy integration system written in Java. As our application was a middleware that did not perform significant data calculations, the decision to use node.js and TypeScript was justified. The immediate benefits were in terms of memory usage. Previously, we allocated a maximum of 4 GB of memory and 6 CPUs for our Kubernetes containers while using Java. After switching to node.js, the maximum use dropped to 700 MB and 0.5 CPUs.

Coming from the Java ecosystem, I was hesitant to embrace JavaScript/TypeScript for our new project, but the process was streamlined thanks to the amazing help of our SSENSE dev community. There’s nothing like “jumping right in” to learn practical skills, such as writing code in a new language. I was pleasantly surprised by TypeScript’s ability to disguise itself as Java.

Similarities Between Java and TS

There are several similarities between Java and TypeScript. Curly brackets for code blocks, semi-colon for line delimiter, conditional and loop statements, and return programming structures are all identical in both languages. The idea of classes, inheritance, polymorphism and encapsulation are all ideas used in both object-oriented programming languages. These similarities made comprehending TypeScript code easy for me.

Let’s look at the way Java and TypeScript let you create classes:

In Java

In TypeScript

Both pieces of code are eerily similar.

Differences Between Java and TS

Let’s begin with the basic differences between Java and TypeScript.

Type Checking:

To understand type checking, we must first understand how compilers operate. Before translating the programmer’s code into low-level bytecode, all compilers now convert it into a data structure called Abstract Syntax Tree. The following steps will be followed when compiling TypeScript code:

1. TS source code converted to TS AST

2. AST gets checked by the type checker

3. AST gets converted to JS source code

4. JS source code converted to JS AST

5. AST gets converted to bytecode

6. Bytecode is evaluated in runtime (Google’s V8 engine)

The same process gets followed for Java too. Changing a variable’s type does not affect the JS bytecode because the compiler does not consider the types in Step 3. For Java, changing the types will change the bytecode. So, you can play with your types without breaking your runtime bytecode. You can even omit the data type in some cases.

Structural vs Nominal Type System:

There is a fundamental difference between how the types are inferred in Java vs TS. In TS if the underneath structure of 2 objects is the same then they are considered to be equal even if they are not of the same type. On the other hand, Java is a nominally-typed language, the same comparison is done based on the types of the 2 objects. This article explains the differences between the 2 typing systems in greater detail.

Primitive Types:

In Java, there are 8 primitive data types vs 7 in JavaScript. JavaScript’s number data type does not make a distinction between numbers and can handle all of Java’s numeric types (int, long, float, double, short, and byte). This leads to less confusion for developers when selecting a suitable type for their variables, but it also leads to always using 64-bits for numbers irrespective of how big or small the value stored in it is. However, Java’s massive memory footprint has given us these diverse numeric data types, which JS developers rarely have to worry about.

Concurrency:

Since its inception in 1996, Java has supported concurrency. Almost all modern Operating systems have a concept of Processes and Threads to achieve concurrency. Threads are execution streams within a process. Every process will have its own memory space, whereas threads within a process will share the memory of the process. This is important to understand the difference between how asynchronous execution works in node.js and JVM.

There are a lot of ways to achieve concurrency in Java, but all of them manipulate threads to be able to achieve it. The low-level concurrency way is to spawn a new thread.

Before we go any further, it’s important to realize that Java’s bytecode interpreter runs on the main thread and begins reading and executing the bytecode in the main() method.

Creating a new Thread using this code:

JVM’s bytecode interpreter will delegate this to the operating system and a new OS thread is created which is a new copy of the JVM bytecode interpreter. The new interpreter will start executing whatever code is inside our lambda function. This new thread will have access to CPU time through time slices of processor time and will have access to shared memory between other threads.

Node.js, on the other hand, does not support concurrency in the same way that Java does. To begin, we must realize that JavaScript’s V8 engine only employs one thread and that this primary thread will be used extremely deftly to create the illusion of concurrency. This is achieved through the event loop.

For this piece of code, the execution is as follows:

1. The main JS thread will call the natively asynchronous API setTimeout. After the call, controls are retained by the main thread and the program continues as if nothing happened.

2. The next line prints “Hello” to the console.

3. After our defined time of 10 ms, that asynchronous call from step 2 gets completed and a task is put in the event queue including some metadata about the call and its callback.

4. Whenever the main JS thread’s call stack is empty, it will check for any pending tasks in the event queue and start executing them.

This unending loop, in which the main thread keeps executing tasks until the call-stack and the event queue is empty, is called event-loop.

This is a very different approach to “multithreading”, but it has its own set of advantages. If you don’t do it appropriately, multithreading in Java can cause a lot of problems. Threads can get stuck in a catch-22 position and deadlock, in which case each thread waits for the other to do its operation if shared memory is not locked/unlocked correctly. Debugging and fixing such codes is difficult. One of the most well-known issues is that in Java two threads can alter a long or double at the same time.

This event-loop and the single-threaded nature of TypeScript are the reasons why we don’t need to protect against race conditions as the JS runtime only processes one message at a time from the stack or queue. In Java, we would need to protect a variable from race conditions with a mutex or a semaphore but all that is not needed by TypeScript.

Once you understand the async/await keywords and how they are used in this context, concurrency in TypeScript is a delight. It is much more foolproof than in Java. You can read more about it in this article by one of my colleagues.

Conclusion

To summarize, a Java developer will have no trouble understanding TypeScript syntax. Although TypeScript has some quirks, my overall experience with this transition has been fantastic. As long as you understand TypeScript’s strengths and limitations, it is a highly useful language.

Much love to both Java and TypeScript 🖤

Editorial reviews by Catherine Heim, Pablo Martinez & Mario Bittencourt.

Want to work with us? Click here to see all open positions at SSENSE!

--

--