article

Adding types to your JavaScript project with TypeScript

JavaScript has been the core part of frontend development since the advent of frameworks such as Angular, React, and Vue. It’s a dynamically-typed programming language, meaning that a variable that was defined as an integer in the beginning of the code can be reassigned as a string value to no harm.

Being this permissive in smaller codebases generally works fine. However, working without type restrictions will probably come to bite you back in the long term when dealing with larger projects.

Enter TypeScript, a superset of JavaScript which compiles to plain JS and that introduces the concepts of static typing and typechecking to the workflow. TypeScript will validate your code at compilation time to check if everything is orderly and will throw an error if it finds something wrong.

But what does it evaluate, exactly? Well, suppose you have the following piece of code:

let foo = [“Hello”, “World”];
foo = “Hello World”;

If you’re working on a full plain JS project, that code is perfectly fine. You instantiated the foo variable with an array value and then assigned a string to it. When you’re dealing with TypeScript the problem is you aren’t allowed to modify the type of a variable later on. But why is that behavior important? If you accidentally converted the array into a string and then tried to loop through it, your code would crash at that point.

With TypeScript, you’re not allowed to change a variable type and you would be given an error at compilation time, which means you won’t be permitted to even run the code until you fix the error. If you’re using a compatible code editor (i.e.: VS Code), you’ll be able to see the errors in real time while you are coding, much like when you see linting errors from ESLint, so you can fix the issues in advance.

BENEFITS

By introducing TypeScript to your project, your code will be analyzed typewise ahead of runtime to see if there’s something that will certainly break when executed. This will allow you to be more confident that your code will act as expected. For example, you’ll be able to detect at writing time if you’re correctly structuring an object or if there’s a typo in one of the fields. On a typeless application, you’d only be able to face it at execution time. This will lessen the number of bugs you’ll face during execution.

You will also notice that a compatible IDE IntelliSense (such as VS Code) integrates quite perfectly with TypeScript, giving more accurate code completion suggestions. This is possible because the IDE will check ahead the interface files and detect which properties are available, their typings and if they are required or not, which help in speeding up your coding.

By adding types to your variables and functions, you will also be developing a “self documented code”, improving readability and maintainability. When you or other developers come back to the code in the future, it will be easier to understand what the code is doing and which types of parameters and variables are being passed around.

This is also extremely helpful for external developers. They will be able to see with ease  what they are supposed to pass as parameters for a function, and won’t need to read through the documentation to check for information.

You can even keep “typeless” JS files in your project. It could become very complicated if you had to refactor every file of your project to use TypeScript. You can keep all your JS files intact and start writing new files using TS syntax. When you have spare time, you can start refactoring files to adopt TS across all the project. This allows you to concentrate first on what may be crucial for your project (i.e.: adding new critical features) and later on work on adding typechecking to it.

Since TypeScript is a superset of JavaScript, no changes are actually needed at first when migrating from one to the other, making this process easier. Remember, in the end everything will be compiled into plain JS.

TypeScript even allows you to write typeless code, using the any wildcard typing. One could argue this would be counterproductive, but sometimes you may have to use a permissive type in a variable or a function simply because it’s too hard (or near impossible) to define a typing and you’d waste time trying to do so.

This necessity to sometimes use a more permissive typing is explained in this talk, which happened at Vinta's PythonXP event (it's worth noting that this video is currently available in portuguese only, but it's being gradually subtitled for other languages). Even though the talk itself isn't about TypeScript, the concepts there can be applied for our context as well.

DRAWBACKS

The most visible drawback when using TypeScript in a project is that you’ll inevitably have to write more code to specify the variable typings. If you’re using TS in a React project, when creating a new component you’ll need to provide an interface to the props, similar to how it’s done here. Typescript also adds a few new concepts, like Generics, Interfaces, Enums, and Unions. These two aspects may slow down the development, especially when you’re just starting to use TypeScript.

TypeScript has a problem when dealing with “runtime generated data”. Imagine that a part of your code performs an asynchronous request to a REST API and then stores the response into a variable. But what kind of properties does the response contain? As of now, your compiler can’t magically predict what kind of data you’re consuming from an async request, so it basically delegates this responsibility to you.

To mitigate this issue, you can implement an interface for the expected response that will be sent from an async request. This will help your IDE in providing better options when developing, and will likely help diminish the number of errors. You can get a better explanation on this here. However, if the API changes the response interface for any reason at a given moment, your code will possibly break at runtime.

USING TYPESCRIPT WITH REACT

As mentioned above, you can use TypeScript in a React project. If you use create-react-app to start a new project, it can be done simply. You just need to pass the --template typescript flag when bootstrapping the project with npm or yarn, as shown here. If you’re developing a standalone app (or just want to add TypeScript to an existing project), you can follow this guide on how to configure it using Webpack.

SHOULD YOU ADOPT TYPESCRIPT?

One of the pressing concerns of application development presently is that time should be spent developing new features and not fixing bugs. Adopting modern technologies, such as TypeScript, increases software reliability and developers can be assured that their code won’t break because of type errors, which are now caught at compilation time. Adding types to your code may also help other developers working on the same project, lessening the time they will spend figuring out what the code does.

There are some situations, however, where using TypeScript may not be worth it. If you are developing a smaller application, or even working alone in a project, maybe the time you spend configuring your workflow will be longer than the time you effectively spend developing.

USEFUL ARTICLES

  1. Configuring React+Webpack projects to use TypeScript
  2. React + TypeScript Cheatsheet
  3. Learn TypeScript Linting
  4. https://dev.to/busypeoples/notes-on-typescript-pick-exclude-and-higher-order-components-40cp
  5. https://twitter.com/acemarke/status/1199349260107571200
  6. async/await with static type checking
  7. Small PoC project using React+TypeScript+async/await
  8. Publishing a TypeScript package on NPM

Renato Vieira

Fullstack Developer at Vinta Software.