Introduction to TypeScript: Type Inference In Practice

3 August 2015, by Tim Perry

TypeScript is a powerful compile-to-JS language for the browser and node, designed to act as a superset of JavaScript, with optional static type annotations. We touched on it years ago when it was first released, but version 1.5 is coming soon, and it’s time for a closer look!

In this post, I want to first take a look at what type inference TypeScript gives you on vanilla JS, and why you that might be something to care about, before we later start digging into the extra constructs TypeScript provides.

What is TypeScript?

TypeScript as a language is very similar to JavaScript, but bringing in type inference, access to future features (due to the compile step), support for more structure, and optional type annotations, all while remaining a strict superset of JavaScript.

Type inference and annotation are the killer features here: it allows you to annotate variables with these type annotations as you see fit, and then uses an extremely powerful type inference engine to automatically infer types for much of the rest of your code from there, automatically catching whole classes of bugs for you immediately. This is totally optional though, and any variables without types are implicitly assigned the ‘any’ type, opting them out of type checks entirely, allowing you to progressively add types to your codebase only where they’re useful, according to your preferences.

Catching bugs for free (almost) with static type inference

Below is a very simple example chunk of vanilla standalone JavaScript. There are a selection of showstopping bugs in the below; without reading on, how many can you spot?

navigator.geolocation.getCurrentPosition(function onSuccess(position) { var lat = position.latitude; var long = position.longitude; var lastUpdated = Date.parse(position.timestamp); var now = new Date(); var positionIsCurrent = now.getYear() === lastUpdated.getYear(); if (positionIsCurrent) { var div = document.createElement("div"); div.class = "message"; div.style = "width: 100%; height: 100px; background-color: red;"; div.text = "Up to date position: " + lat + ", " + long; document.body.append(div); } else { var messageDivs = document.querySelectorAll("div.message"); messageDivs.forEach(function (message) { message.style.display = false; }); } }, { enableHighAccuracy: "never" });

Done?

In total there’s actually 12 bugs in here, ranging from the easily spottable (it’s .appendNode() not append(), which will crash with an undefined method exception as soon as you start testing), to the more subtle (enableHighAccuracy is a boolean and “never” is truthy, so this unintentionally turns on your GPS and waits until the position is accurate enough, and various assignments to incorrect properties will just silently do nothing). All 12 of these are caught by TypeScript automatically however, when run on this vanilla JavaScript source just with type inference, no type annotations.

Take a look at a commented version for some more details, with these issues automatically caught by the TypeScript compiler, in the TypeScript playground at http://goo.gl/L9qp8o.

This then gets even more powerful once you do start annotating parameters, to provide more information in the cases it can’t automatically spot for you. Of these, how much time would it take to trace these bugs down? How does a compiler that points out each of these half a second after you add it sound?

Types are powerful. They don’t catch all bugs by any means, and good test coverage remains important, but type checking is an easy and effective method to totally immediately remove an entire class of bugs from your codebase, to let your testing instead focus on actual functionality and behaviour, rather than just checking your code is sensical.

A key point here is the speed of feedback: writing + running your tests is always going to be a slower and far more time consuming process than compiling your code, and good IDEs (both Visual Studio and Intellij) will give you line by line feedback in sub-second times when you write code that won’t compile. This is something that great tools like JSHint can provide, but while they’re definitely useful, without understanding the types in a codebase they’re severely hampered. In the above code, JSHint sees no issues whatsoever.

TypeScript meanwhile catches these issues, working with your vanilla JS code out of this box, for near-zero effort (near-zero: there’s a small bit of setup required to add the compile step, but very very small, and it shouldn’t require any code changes). This is no panacea, there’ll still be bugs and you’ll still need to test your code heavily, but with types you’ll at least already have the confidence that there’s nothing totally drastically wrong before you do so.

Going beyond this

That’s enough for one post, but hopefully starts to give you a taste of what TypeScript can provide above and beyond JavaScript.

This post is the start of a series on TypeScript; in the next post we’ll take a look at type annotations, and how you can extend this power to cover all your code, not just code where the types are easily inferrable, to take this even further. If you’re hungry for more in the meantime, take a look at TypeScriptLang.org for the full guide into the details of how TypeScript works, or ping us on Twitter with your questions and thoughts.

Tags: JavaScript, programming, technical, types, typescript

Categories: Technical