Is TypeScript better than JavaScript?

Since I started developing web applications, I have been hearing comments like "TypeError: Cannot read property 'something' of undefined, again??" or "Each time I touch something, something else breaks!". Today, I want to talk about TypeScript: a technology that enables a huge amount of tools that ultimately improves the developer experience to a very superior level. The result? Better delivery of highly scalable, and, in the end, finer software.

What is TypeScript at all?

TypeScript is JavaScript that scales. TypeScript is a typed superset of JavaScript that compiles to plain JavaScript.  - TypeScript Docs

For me, TypeScript is just a type linting tool. It is not another compile-to-js language that aims to "fix JavaScript". Instead, it sits on top of it, giving you insight on the types. Kind of the same work that ESLint does by giving you feedback if you use bad practices.

If you are JavaScripting, you are already TypeScripting

I bet you are using some editor or IDE like VSCode. In that case, you are already using TypeScript.

VSCode internally uses TypeScript's engine to give you recommendations, even if you are using plain old JavaScript. While the snippet of code that you are working on is easy enough, it will provide IntelliSense recommendations.

If you use JSDoc for creating documentation, the TypeScript engine can read these comments to help you with your code types.

Features of TypeScript

Type checking

First of all, TypeScript will warn you about any incorrect type in your functions or variable declarations. This is useful when we have complex parameters in an imported function.

We will be able to detect these kinds of errors during compile time, or even earlier thanks to the IDE integration.

Null safety

How many times have you come across TypeError: Cannot read property 'something' of undefined? This is a common problem in JavaScript: we aim to read the key of an object, but at that point of execution it is undefined. This error might happen, for example, when searching for an item in a list.

const persons = [{ name: "John" }, { name: "Jorge" }];
const peter = persons.find(p => p.name === "Peter");
console.log(peter.name);
// Uncaught TypeError: Cannot read property 'name' of undefined

The code above will crash during execution. In the code, we forgot to add a null-check in case Peter is missing. We will most likely notice this mistake during runtime. It could even crash in our production environment thus we could be forced to wait until the next deployment.

With TypeScript, we get instant feedback on this without the need of running the code or tests.

Once we are familiarised with the use of TypeScript and IntelliSense, we will catch the error sooner. The hint to guess that peter might be null is that VSCode will not suggest the name key after typing the point to complete the statement. It also helps us to remember to use await when we are dealing with promises. Sometimes I forget this statement. When I don't get IntelliSense recommendations on my results I know something is wrong.

IntelliSense

IntelliSense is a code completion tool built by Microsoft. It helps us to type faster and it alleviates the cognitive charge of remembering the types of data.

In the following image, TypeScript knows that the parameter people is an array, so when I type the letter "m", it recommends both map and flatMap methods. Even more, inside the map callback, TypeScript knows that each item of the array is an object of type People, so it recommends the name key.

Docs on your fingerprints

One of the boring parts of starting to play with new technology is getting used to its methods and functions. Going back and forth into the docs might be tedious.

One of the features that I like about TypeScript is the documentation propagation inside the IDE.

type Config = {
  /**
   * This is the hostname of the **DataBase**. And yes! *the docs* `supports Markdown`!
   */
  hostname: string;
};

If we have a class/function/type/variable documented with JSDoc, we can retrieve it from anywhere in the code.

Suggestions and Refactors

If we write code with TypeScript, IntelliSense will have a much deeper understanding of our codebase, and it will give us more accurate suggestions when we are writing code. My favourite suggestions are:

  • Automatic Imports: when we start typing the name of a symbol that we have not imported yet, TypeScript will recommend us by placing an automatic import at the top of the file.
  • Smart rename: instead of finding and replacing, which can lead you to rename an element by mistake, we can use the rename refactor tool by pressing F2 over a symbol. As TypeScript knows every usage of that symbol, it can perform the refactoring without changing things with a similar name. I find this very useful when refactoring React component props.
  • Automatically fixing imports when moving files: this is one of the features that I like the most. It is exceptionally useful for me when I'm moving React components into other directories.
  • Remove unused variables and unused imports: sometimes I create temporal variables to display their content using console.logs. When I no longer need them, I can remove all the unused variables, methods and imports at once by using "Delete all unused declarations".
  • Creating methods that do not exist on the fly: when we type a new method that does not exist yet in a class instance, TypeScript displays a "Declare method" option in the suggestion menu. If we select this option, TypeScript will write it for us. This is quite convenient if you are using TDD.
  • Extract into variable, method or constant: we can extract statements into variables or constants and we can even extract complete blocks of code into methods or functions.

Even more refactor with WebStorm

In addition to all the VSCode refactor features, WebStorm provides access to the refactor engine of JetBrains. This engine has even more insight and has greater refactoring tools than VSCode.

  • Unused public methods: unlike VSCode, WebStorm alerts us when exporting an unused piece of code.
  • Inline methods: this refactor consists of inlining the content of a method or function in their usages. This is also one of the features I'm missing in the in the TypeScript refactoring engine.

TypeScript keeps your codebase consistent

When we have type safety, we make sure that all the pieces of our software are matching together at all levels. This is very useful when dealing with large teams. One of the members of the team might rename a function and we might not realise that the change is in our branch. But after merging, TypeScript will warn us about the mismatch.

With TypeScript, we will have instant feedback if any two-parts of code don't match.

Remove unnecessary type limit cases

Something very common in JavaScript is to code defensively. For example, if we are writing a library, we cannot tell if the user will send us the correct input type. For this situation, we should add a type guard to make sure that we have the correct input.

I even know people that write tests to check how their functions react when receiving unexpected types of data.

These kinds of tests and guards no longer make sense with TypeScript, as we are enforcing us to use the correct data types.

Slow adaptation and fast hacking

"I'm not sure how to do this in TypeScript, let's use any"... "I just want to test some quick thing, so I'm placing an any here". Some people get mad when their coworkers don't use the right typings. I think that these kinds of comments are perfectly valid.

When we are learning TypeScript, we might not be able to achieve some things that the language asks us, and we need to get the job done. In these situations, we can use the any type or a @ts-ignore to skip the typing check. Later, we can come back and code things right (even if we need to ask someone for help).

Also, if you need to hack some piece of code quickly, you might skip type checks. When you accomplish your goal, you can review your code and put some effort into using the right types.

More and more companies are betting on TypeScript

JavaScript developers have been recognising the power and benefits of TypeScript. In particular, developers in large-scale projects are including TypeScript to their stack. Here are some projects that use TypeScript under the hood:

  • Visual Studio Code
  • Grafana
  • Angular
  • Vue 3
  • Svelte
  • Apollo GraphQL
  • Deno

This is not a fantasy land

Though TypeScript has its benefits, it also comes with some trade-offs.

TypeScript tends to be more verbose than JavaScript, especially when we get into more complex things like Generics. When we begin to work with TypeScript, we might get a bit lost applying several patterns like type guards.

So, what are the conclusions the Acid Tango team reaches?

Sure, working with TypeScript is quite great. All in all, we are big advocators of this programming language since it features:

  • Linting tool for types
  • Great for the maintainability of long-term projects.
  • Take advantage of in-editor suggestions/refactors
  • Catch errors in compile-time or while working on your editor.
  • Easy integration with build tools (Webpack, Rollup...)

Try it on your own! In Acid Tango we have written a small boilerplate of TypeScript for making exercises or small projects.

https://gitlab.com/acid-tango/boilerplates/typescript

Give it a try, share your results, and tag us so we can see how it went!