TypeScript: JavaScript + Types = Awesome Developer Productivity
The Mastering the Arcane Art of JavaScript-mancy series are my humble attempt at bringing my love for JavaScript to all other C# developers that haven’t yet discovered how awesome this language and its whole ecosystem are. These articles are excerpts of the super duper awesome JavaScript-Mancy book a compendium of all things JavaScript for C# developers.
TypeScript is a superset of JavaScript that adds type annotations and, thus, static typing on top of JavaScript.
If you are a C# or Java developer you’ll feel right at home writing TypeScript. If you are a JavaScript developer or have a background in dynamic programming languages you’ll encounter a slightly more verbose version of JavaScript that results in a safer and better developer experience. Either way, you’ll be happy to know that everything you’ve learned about JavaScript thus far also applies to TypeScript, that is, any JavaScript is valid TypeScript.
Any JavaScript is Valid TypeScript
Any bit of JavaScript is valid TypeScript. Let’s say that we have the most basic piece of JavaScript code that you can write, a simple variable declaration that represents your reserve of mana:
var manaReserves = 10
And now let’s say that we want to recharge your mana reserves by drinking a magic potion:
function rechargeMana(potion) {
return potion.manaModifier * (Math.floor(Math.rand() * 10) + 1)
}
So we go and write the following:
manaReserves += rechargeMana({
name: 'light potion of mana',
manaModifier: 1.5,
})
When we execute the piece of code above, it explodes with the following error:
// => Uncaught TypeError: Math.rand is not a function
Which makes sense because there’s no such thing as a Math.rand
function in JavaScript. It is called Math.random
. For some reason I mix this function with a C function that has the same purpose, a slightly different name, and which I used in my student days. Regardless, I make this mistake, again and again.
The code above is a very traditional piece of JavaScript. But it is also completely valid TypeScript, with one difference, writing the rechargeMana
in TypeScript would have automatically resulted in a compiler error that would’ve read:
Property 'rand' does not exist on type 'Math'.
This would have immediately alerted me to the fact that I’m making a mistake (again), and I would have been able to fix it before executing the program. This is one of the advantages of TypeScript: shorter feedback loops where you can detect errors in your code at compile time instead of at runtime.
Let’s expand our previous example and drink another potion:
rechagreMana({
name: 'Greater Potion of Mana',
manaModifier: 2,
})
Again. A simple typo, a classic mistake in JavaScript that would result in a ReferenceError
at runtime, is instantly caught by the TypeScript compiler:
Cannot find name 'rechagreMana'.
As we’ve seen thus far, the TypeScript compiler that sits between the TypeScript code that you write and the output that runs in the browser can do a lot of things for you on vanilla JavaScript. But it truly shines when you start adding type annotations, that is, when you annotate your JavaScript code with additional bits of information regarding the type of things.
For instance, let’s update our original rechargeMana
function with some type annotations:
function rechargeMana(potion: { manaModifier: number }) {
return potion.manaModifier * (Math.floor(Math.random() * 10) + 1)
}
The example above contains a type annotation for the potion
parameter {manaModifier : number}
. This annotation means that the potion
parameter is expected to be an object that has a property manaModifier
of type number
.
The type annotation does several things for us:
- It can help the compiler discover errors when the object passed as an argument to
rechargeMana
doesn’t have the expected interface. That is, when it lacks themanaModifier
property which is necessary for the function to work. - It can help the compiler discover typos or type errors when you use the
potion
object within the body of the function. - It gives us statement completion when typing
potion
inside therechargeMana
function which is a great developer experiencestatementcompletion. If you aren’t familiar with statement completion it consist on helpful in-editor information that pops up and tells you how you can use an object, like which properties are methods are available, which types are expected for the different parameters, etc.
The editor that you use should have a good integration with the TypeScript compiler to provide this type of service. Many of the most common IDEs and text editors have that support.
Let’s illustrate 1) with an example. Imagine that in addition to potions of Mana you had potions of Strength:
const potionOfStrength = {
name: 'Potion of Strength',
strengthModifier: 3,
duration: 10,
}
At some point in our program we could end up calling this code by mistake:
rechargeMana(potionOfStrength)
Calling the rechargeMana
function with a potionOfStrength
as argument would result in a runtime error in JavaScript or, perhaps even in an elusive bug since multiplying undefined
by a number
results in NaN
instead of crashing outright.
In TypeScript however, the example above would result in the following compiler error:
// [ts] Argument of type '{ name: string; strengthModifier: number; }'
// is not assignable to parameter of type '{ manaModifier: number; }'.
// Property 'manaModifier' is missing
// in type '{ name: string; strengthModifier: number; }'.
This error would quickly tell me that the potion of strength lacks the required contract to use rechargeMana
and lots of tears and frustration would’ve been saved right then and there. Also take a second to appreciate the quality and precision of the error message above.
So any JavaScript is valid TypeScript. Change your code.js
file into code.ts
file, run it by the TypeScript compiler and TypeScript will try to infer the most information it can from your code and do its best to help you. Add type annotations on top of that and TypeScript will be able to learn more about your code and intentions, and provide you with better support.
So, What Are The Advantages and Disadvantages of TypeScript?
By enhancing your JavaScript with type annotations and static typing TypeScript provides these advantages:
- Better error detection. TypeScript can do static analysis of your code and reveal errors before running the actual code. This provides a much shorter feedback loop so that you can fix these errors as soon as they happen inside your editor and not after they hit production.
- Better tooling and developer productivity. The rich type information can be used by editors and IDEs to provide great tooling to enhance your developer productivity like in-editor compiler warnings, statement completion, safe refactorings, inline documentation, etc… Visual Studio Code is a text editor that has awesome TypeScript support out of the box.
- Great API discoverability. Using statement completion provided by type annotations is an outstanding way to discover about new APIs right inside your editor.
- Write more intentional code. TypeScript type annotations and additional features like access level keywords allow you to constrain how the APIs that you design are meant to be used. This allows you to write more intentional code.
- ESnext features. TypeScript supports a lot of ESnext features like class members, decorators and
async/await
. - Additional TypeScript Features. In addition to JavaScript and ESnext features TypeScript has a small number of features that are not in the ECMA-262 specification which add a lot to the language like property access levels and parameter properties.
- Works with third-party libraries. Using type annotations in your application code is awesome but what about all the third-party libraries that you use and are reference throughout your application code? How does TypeScript interact with them? Particularly, what happens when these libraries aren’t written in TypeScript? In the worst case scenario TypeScript treats objects it doesn’t know as of type
any
which basically means “this object can have any shape so just behave as you would in JavaScript and don’t make any assumptions”. More commonly, third-party libraries either come with declaration files that provide typing information for TypeScript or you can find these declaration files through the DefinitelyTyped project a repository of TypeScript type definitions. This means that you’ll be able to enjoy the same level of TypeScript support (or even greater) for third-party libraries that you do for your own code. - Great for large-scale applications and teams. TypeScript excels at supporting multiple teams with large-scale applications. The type annotations and the TypeScript compiler are awesome at catching breaking changes, subtle bugs and with new APIs discoverability.
On the minus side:
- TypeScript requires a transpilation step. TypeScript code is not supported as-is in any browser. In order to be able to write your applications in TypeScript you need to setup some sort of build pipeline to transpile your TypeScript code into a version of JavaScript that can run in the browser. Fortunately, there is great support for this in the open source community and you can find great integrations for TypeScript in the most popular frameworks and build tools.
- You need to learn type annotations syntax and related artifacts. The type annotations, their syntax and related artifacts like interfaces, generics, etc… add more cognitive load and an extra degree of complexity on top of all you need to know to write JavaScript applications.
- It is verbose. The addition of type annotations makes your JavaScript code more verbose (
call(person:Person)
) which can be quite aesthetically unpleasing (particularly at first). The TypeScript compiler does a great job at inferring types and reducing the amount of type annotations you need to write to the minimum but to make the most out of TypeScript you’ll need to add a fair amount of type annotations yourself.
Up Next
Up next you’ll learn how to create a simple TypeScript project and learn some of the cool features available in TypeScript.
Get the Book!
If you enjoyed this article take a look at the JavaScript-mancy OOP: Mastering the Arcane Art of Summoning Objects, a compendium of OOP techniques available in JavaScript, ES2015, ESNext and TypeScript.
Have an awesome day! :)
More Articles In This Series
- Chapter 0: The Basic Ingredients of JavaScriptMancy
- Chapter 1: The Many a One JavaScript Quirks
- Functions
- Object Oriented Programming
- Basics
- ES6
- OOP
- Introduction to OOP in JavaScript for C# Developers
- Summoning Fundamentals: Introduction to OOP In JavaScript – Encapsulation
- Summoning Fundamentals: Introduction to OOP In JavaScript – Inheritance
- Summoning Fundamentals: Introduction to OOP In JavaScript – Polymorphism
- White Tower Summoning: Mimicking C# Classical Inheritance in Javascript
- White Tower Summoning Enhanced: The Marvels of ES6 Classes
- Black Tower Summoning: Object Composition with Mixins
- Safer JavaScript Composition With Traits and Traits.js
- JavaScript Ultra Flexible Object Oriented Programming with Stamps
- Object Oriented JavaScript for C# Programmers
- Tooling
- Data Structures
- Functional Programming
- And more stuff to be shaped out:
- Functional Programming with JavaScript
- Modules
- Asynchronous Programming
- The JavaScript Ecosystem
Written by Jaime González García , dad, husband, software engineer, ux designer, amateur pixel artist, tinkerer and master of the arcane arts. You can also find him on Twitter jabbering about random stuff.