Looking for a quick answer? Here it is.

TypeScript is 100% worth it. It's how JavaScript should have been by default. The combination of static type checking with intelligent code completion provides a significant boost in developer productivity. Not only can you work faster, but you can also catch a ton of errors before they arise.

If that answer doesn't satisfy you, read on and I'll show you why.

TypeScript gets a lot of love and some hate. I've seen quite a few questions posted online asking if it's worth learning TypeScript or if TypeScript is better than JavaScript.

What I've found is TypeScript is a lot like Git in terms of how you will feel about it. Remember the first time you heard of Git? Chances are you were wondering why you even need it or if the extra complexity worth it. Then you start to learn about it. At first, it feels pretty hard to grasp. After using it for some time, you slowly start to fall in love. Fast forward a year, and you couldn't ever imagine coding without it.

That's exactly how TypeScript will feel. Once you get into it, you will never want to write plain JavaScript again.

Before we get into the advantages and disadvantages of TypeScript, let's recap the basics.

What is TypeScript?

TypeScript is a superset of the JavaScript programming language that enables strong static typing. Let's define strong static typing.

Strong typing refers to a programming language enforcing stricter standards such as not allowing implicit type conversions. For example, in a strongly typed language, you cannot add the string "5" with the number 5.

Static typing refers to type checking that occurs at compile time instead of runtime. Type checking means checking for invalid type usages such as adding a string and number.

JavaScript by default is a weak dynamic typed language. This means everything is evaluated at runtime and it will perform implicit type conversions. For example in JavaScript, the following code is valid and would work fine.

JS
console.log(5 + [] + 5);
// logs: "55"

The above code runs without an error because JavaScript is able to use the .toString() function to concatenate the variables together. In this case the [].toString() results in an empty string and (5).toString results in the string "5". So it ends up computing "5" + "" + "5" resulting in "55".

I think there is a big problem with code like that as it can quickly produce unexpected errors that could otherwise be identified easily. Maybe you like the flexibility. However, you could easily be explicit about it instead of using implicit conversion as a feature. By being explicit, I think you would be doing everyone a good service, including your future self who won't even understand the code a year from now.

One other key point to keep in mind is since TypeScript is compiled down to JavaScript, ultimately your code will be executed in a weak dynamic context. That means "errors" and bad programming practices can still occur. However, as long as you use TypeScript properly, that's highly unlikely or at least more predictable.

Anyway, the above example might seem trivial. But once you see how this concept extends around the whole language, your mind will be blown. Before we get there, let's learn more about what TypeScript is so you understand the context a bit better.

Superset language

One thing that makes TypeScript so awesome is it's a superset of the JavaScript programming language. What this means is pure JavaScript is 100% compatible with TypeScript. You can take your current .js file and change the extension to .ts and start using TypeScript.

Thanks to this, there is no need to re-write any code (though you will likely have errors to fix). Thanks to this compatibility, you can also use any package or library from the JavaScript ecosystem.

TypeScript is also committed to being completely compatible with JavaScript and all its upcoming additions. This way, you are never locked into it. You can easily refactor your application to plain JavaScript.

Using TypeScript

Using TypeScript is fairly simple once you get the hang of it. You can get started by installing the typescript NPM package. Create an index.ts file and add a build script with the content tsc index.ts in your package.json.

Once you add code to your .ts file, run the build script to output a index.js file. You can then execute the .js file however you like such as in a browser or using Node.js.

You can also use a package like ts-node that can execute a TypeScript file directly. Alternatively, you can integrate TypeScript with Webpack and other build tools.

Realistically, you will need to create a tsconfig.json file and learn about the various compiler options to get your desired result. This article won't cover that, so you should look elsewhere to figure that out.

Advantages of TypeScript

So now that you understand the basics, let us look at the major benefits of using TypeScript.

No more silly mistakes

As you have already seen from the example above, TypeScript will catch simple mistakes such as adding an array with a number. When strong typing is enabled, types will be checked across our entire codebase, even in function arguments. For example, consider a simple add function. In JS, we would write it like this:

JS
function add(a, b) {
return a + b;
}
console.log(add(1, 2)); // valid
console.log(add(1, [])); // should be invalid
const result = add(1, 2);
console.log(result + 3); // valid
console.log(result + []); // should be invalid since result is a number

As you can see, an error could be made by submitting the wrong argument type to the function or by incorrectly using the return type of the function. This code can easily be re-written with TypeScript like this.

TS
function add(a: number, b: number): number {
return a + b;
}
console.log(add(1, 2)); // valid
console.log(add(1, [])); // marked as invalid
const result = add(1, 2);
console.log(result + 3); // valid
console.log(result + []); // marked as invalid

By sprinkling some types onto our function declaration, TypeScript will be able to catch any invalid argument types passed to a function. It also knows that the add function returns a number. So the result cannot be added to an array as that doesn't make any sense.

Likewise, TypeScript will also prevent you from accessing things that don't exist. For example, consider this code.

TS
const shirt = { color: "red", size: 5 }
console.log(shirt.color) // valid
console.log(shirt.colour) // marked invalid as it doesn't exist
console.log(shirt.size + "5") // marked invalid, you can't add number to string

As you can imagine, this ability to know exactly what's defined where will prevent you from numerous spelling errors or incorrect type usage.

Type inference

Unlike some strongly typed languages that require you to explicitly annotate the type of everything, TypeScript has a feature called type inference. This means types will be inferred wherever possible. Take the above add function. This can be re-written to the following.

TS
function add(a: number, b: number) {
return a + b;
}

There is no need to specify :number at the end of the function which tells the TypeScript compiler the return type of a function. The reason this is possible is that TypeScript knows the result is based on two numbers being added together. Hence the function result must be a number.

Likewise, when defining a variable, there is no need to specify a type, although you can. Consider the code below, both of the variable declarations are identical. TypeScript simply infers that a must be a number.

TS
const a = 5;
const b: number = 5;

Intelligent code completion

For those of us who learned JavaScript or weakly typed languages first, this feels like a superpower. Imagine knowing exactly what properties are available on every object or what argument types are required for functions. With this, code almost becomes self-documenting.

You no longer need to look up the API docs to verify you called the correct function with the correct arguments. Not only do you save time but you avoid a ton of errors.

TypeScript together with an IDE enables this functionality. This is made possible because there are type definitions available for all common JavaScript APIs. For this to work, you need to be using an IDE that supports this functionality such as Visual Studio Code or Webstorm. All screenshots below are from Webstorm.

So let's say I want to get the current page path in the browser. I can look down the window.location object and see all the properties available. No need to constantly reference some documentation to make sure you are looking for the right property. On the right side of the image, you can see all return types from the properties or methods.

TypeScript intellisense for window.location

Say I want to navigate to another page using the assign function, I can call it and the IDE with the help of TypeScript can validate my input and even let me know the name of the argument, in this case, url. This way I have some indication of what the argument is for.

TypeScript intellisense window.location.assign

If I was to pass an invalid argument type, such as a number, I know right away that it's incorrect. Hovering the mouse over the error will reveal the details explaining that 5 is not assignable to a string.

TypeScript intellisense error for window.location.assign

Naturally, if you went to compile the above code with TypeScript, the compiler would let you know about the error and the location it's originating from. The error message you are seeing in the screenshot above TS2345: .... is the exact error that the compiler will spit out.

This code completion is also extremely helpful when working with your own creations. For example, imagine you are creating an online ordering system for restaurants. How would the restaurant model look like? Here is a basic restaurant type defined using an interface. An interface is used to define an object type with a set of properties.

TypeScript restaurant interface

The interface can then be assigned to an object and TypeScript will complain if the object doesn't match that structure or is used inappropriately. To assign the interface, we simply add : Restaurant beside a variable name.

TypeScript assign interface to variable

If we tried to incorrectly initialize the restaurant object, for example defining the postcode as a number instead of a string, we will get an error like this.

TypeScript interface error

Now that the restaurant object is created, we can confidently use it knowing it was initialized correctly. If you wanted to access some of the location data, you would see this.

TypeScript intellisense on object

Let's say I wanted to render a list of dishes with React, I can write my component knowing exactly what properties are available even in the dishes array.

TypeScript react component prop types

This simple feature shines when you are working with extremely complex models. In a real-world scenario, the restaurant model might have dishes with nested options and numerous modifiers. Once things start getting that complex, knowing exactly what's available everywhere is a huge plus.

It gets even better when you are working with 3rd party libraries. If a library ships with TypeScript definitions, which is typically every single popular library these days, you will have complete knowledge of all the APIs it exposes.

For example, take the Google Maps Javascript API. It's so painful to constantly refer to the documentation and ensure your code is correct. With TypeScript, you can install the official google maps type definitions from NPM and start coding away. When you initialize a new map, you can see exactly what options are available and what types are required.

TypeScript intellisense google maps

I hope you can see how just this one feature alone is going to boost your productivity significantly by saving you time and catching all your silly mistakes.

Refactor with confidence

As we figure out better ways of structuring our code, we need to refactor things. It's extremely tiresome to make sure that our code is fully compatible whenever we make a change. With TypeScript, any changes made that are incompatible with the rest of the codebase will be highlighted straight away. This way you can update your code with much greater confidence that everything is handled.

Work easier on large codebases

The larger the codebase, the easier it is to lose track of things. Once again, knowing exactly what's available everywhere means that you can code with confidence even in large codebases without needing to constantly referrer to specific sources.

If you are writing a simple 50-100 line script, TypeScript may not be as useful. However, the second you are creating anything remotely complex that must be production-ready, using TypeScript will give you much greater confidence in your code.

Work effectively in teams

A lot of these benefits are far more pronounced when working with a team of people. With everything strongly typed, changes can be made to the codebase knowing that everyone on the same page. For example, if someone were to change an item price from being stored as a number to a string, the TypeScript compiler will let you know every single spot which is no longer compatible with the new type.

Maximize browser compatibility

One of the best parts about TypeScript is the compiler itself. TypeScript allows you to write code with the latest features of JavaScript such as async / await and have that compile down to ES5 JavaScript. This is done by replacing newer syntax with ES5 compliant code.

Keep in mind, TypeScript won't polyfill APIs like promises or the fetch API. You will need to supply those polyfills if required.

Package JavaScript correctly

The JavaScript ecosystem is very fragmented. When it comes to module systems, we have AMD, UMD, CommonJS, ES6 modules and more. When you use TypeScript, you can specify the output module format for your code. This means you can write all your code using standard ES6 modules which can be compiled down to CommonJS for the Node.js environment and UMD for the browser environment.

Other benefits

  • Write cleaner code because you have complete knowledge of what's available
  • Easier to test code, once again because you have complete knowledge on what's available
  • Awesome compiler with tons of customization options
  • Proven and battle-tested, used by plenty of development teams around the world
  • Easier to onboard developers as everything is almost self-documented

Disadvantage of TypeScript

As amazing as TypeScript is, it does have drawbacks. However, all these drawbacks start to fade away as you become familiar with it.

Learning curve

As simple as it seems, there is quite a bit to it in terms of what you can do. This complexity can result in a lot of confusion initially. There were many instances early on where I had no idea how to apply TypeScript. The official documentation in my opinion was not very easy to read either.

For example, a very common thing that would trip me up is applying any higher-order component (HOC) to my React component. I would constantly get errors with incorrect component types being passed even though everything worked fine. Eventually, I just learnt to ignore certain parts using // @ts-ingnore. Thankfully these issues were not a common occurrence.

Needs to be compiled

No surprise here, TypeScript needs to be compiled down to JavaScript. Neither Node.js nor the browser understands TypeScript natively. Hence you have to compile your code to plain JavaScript before it can run. This can be a bit painful to overcome initially as you learn to adopt it in your workflow.

However, modern JavaScript development tends to already use plenty of build tools like Babel and Webpack. TypeScript can be integrated into most of these tools so it runs alongside your existing build processes. If you already use such tools, then you should feel at home with this concept.

Requires extra setup and configuration

Naturally, we need to configure TypeScript for our environment. You can use it without any configuration but realistically you will need to configure it. I would be lying if I said this didn't confuse me quite a bit at the start. There are a plethora of options available. Thankfully, you only need to configure a few things to get off the ground. Here is an example of a simple TypeScript config file that works for writing Node.js modules.

TStsconfig.json
{
"compilerOptions": {
"outDir": "./dist",
"rootDir": "./src",
"module": "commonjs",
"target": "es6",
"moduleResolution": "node",
"sourceMap": true,
"strict": true,
"lib": ["esnext"]
},
"include": [ "./src/**/*" ]
}

As you can see, not too complicated and pretty straight forward.

Incorrect type definitions

At the end of the day, programmers make mistakes. Occasionally you might come across incorrect type definitions for a 3rd party library. If you trust the definition but something is incorrect, it can trip you up. This issue is only present if the original library wasn't written in TypeScript. If it was, the type definitions will always be in sync.

Conclusion

Hopefully, this article has motivated you to try out TypeScript or give it another chance. As a result of using TypeScript, you will have much greater confidence in your code.

Adopting TypeScript is also something that will benefit the whole JavaScript eco-system and community. If more developers write code in TypeScript, the overall code quality of our eco-system would improve dramatically. Best of all, we would have a fully documented API for every package allowing us to code without constantly referencing the docs.