One Year With TypeScript

fashion-man-person-hand

On the SketchUp team, we’ve been using TypeScript for a multi-thousand line project over the last year. Overall, we’ve been blown away. It met all of our expectations, and it was easy to learn.

Why We Chose TypeScript

We had several hard requirements for frameworks at the beginning of the project:

  1. Static type checking to catch stupid mistakes in JavaScript.
  2. Does not “take over” the code.
  3. Compiles down to a single JavaScript binary.

As a former Google developer, the only option I knew for #1 was the Google Closure Library and compiler.

But I wanted to avoid those tools this time around. Closure is extremely verbose. If you want real type checking, you document the type of each argument to your function in the comments. You also have to specify if the argument is optional, nullable, etc. Even after using Closure for years, I would regularly have to look up the syntax for argument types.

#2 prevented us from using frameworks like Angular. I’ve used Angular for smaller projects, and I liked its power, but I also felt like I was writing in a completely new language.

In the beginning, we were reluctant to use TypeScript because it was new. It wasn’t clear how good the framework was because so few people were using it. But we tried it anyway, and it was good enough to keep using. Fortunately, the Angular team announced they would build Angular 2 using TypeScript shortly afterward. That gave a lot of credibility to our choice.

What We Like

1) Type Checking

The type checking works exactly as expected. It’s caught so many silly bugs and stupid mistakes that I’m sure TypeScript has saved us days of engineering time. For much of the year we were prototyping, meaning our unit test coverage was lacking, so static type checking was essential.

What’s nice is that the TypeScript type checking stays out of your way.

An example function taking a string is:

Compare that to the Google Closure version:

I prefer TypeScript because you use so many fewer characters to accomplish the same thing.

Furthermore, with TypeScript you can omit the types on function arguments (or anywhere else), and you’re left with pure JavaScript.

This is great for a few reasons:

  • Allows easy integration with existing JavaScript code
  • Dealing with raw JavaScript objects is no problem. Passing anonymous objects between libraries or receiving JSON from servers is easy.

In conclusion, the type checking in TypeScript rocks. It does everything you need it to do, and it gets out of your way when you need it to.

Of course, it could also be dangerous. A naive engineer could write all of their code without specifying any type information, at which point the static checking becomes worthless. But overall, I feel TypeScript made a good compromise between the rigidity of static languages and the dynamic nature of JavaScript.

2) Integration with other libraries

We use multiple libraries within our TypeScript project: jQuery, Google Closure, jstree, and several others.

Usually, you can’t expect type checking to work when you’re calling external code. With TypeScript, we don’t have that problem.

The DefinitelyTyped project includes “typings files” for all the most popular JavaScript frameworks, including the entirety of Google Closure (which was shocking to us!).

Typings files enable type checking when you’re calling external functions. If the Typings file is out of date, you get a false compiler error, but this has only happened a handful of times in a year, and it’s easy enough to update the Typings file yourself.

3) Classes

Classes are essential to any JavaScript project greater than a thousand lines. They let you separate everything into multiple files which makes the code more readable.

ECMA 6 will support classes out of the box, but until it’s supported in more browsers, TypeScript allows you to transpile to ECMA 5 compatible code.

The TypeScript compiler turns your class hierarchy into equivalent code using the built-in object prototypes of JavaScript. This runs in all the major browsers, and it’s how our code is shipped.

Debugging the transpiled code is easy because it’s almost exactly the same as what you wrote. The only differences are how the classes are laid out, and transforming things like arrow functions, if you happen to be using them.

4) Arrow Functions

Arrow functions are a neat ECMA 6 feature which takes care of binding “this” for you.

It turns code like this

into something like this

It’s not less code, but it’s a matter of convenience. I always forget to bind the “this” in callbacks, and then I wind up with bugs because I’m calling functions that don’t exist on the global context.

Again, TypeScript transpiles this ECMA 6 feature to the equivalent in ECMA 5, and we can use it in older browsers.

5) Compiling to a single file

We don’t use the fancy importing mechanisms available in TypeScript.

We just specify all of our files in a single tsc command and dump the output to one, final file.

I’ve found a single file is easy to work with in production, as long as it’s relatively small.

What We’ve Struggled With

1) Imports

This is a simple issue, but it threw us off because it doesn’t match languages like Java or C++. Because we specify every file in our project in a single TypeScript compiler command, we never have to explicitly import anything within our files.

This is nice because it’s flexible, but if you want to see which symbols a file is using, you’re stuck with grep’ing.

2) Integration With Other Code

For the most part, code integration works nicely.

Where we’ve run into trouble is when we need to extend the functionality of some other library.

For example, we wanted to extend several objects defined within the Google Closure UI library. So we wound up with a mix of Closure styled code and TypeScript code.

If you’ve used Closure, you know your constructors are surrounded with goog.inherits() clauses. To extend any of those classes, you have to do the same thing, which confuses TypeScript.

We pulled it off, but what we did is hacky. Basically we split a file in half using raw JavaScript and then we built a TypeScript interface to expose it.

Honestly, this is no fault of TypeScript. There’s no good way to support what we were trying to do.

3) Unit Testing

There’s no builtin unit testing framework for TypeScript (yet). There are plenty of third party ones, but I’d love to see something similar to JUnit for building and running tests.


So that’s it. We’ve been using TypeScript for a year, and we’re happy. You should try it in your project too. It’s the best way I’ve seen yet to write and maintain large JavaScript projects.