Having A Pleasant Refactor to Typescript

Working with JavaScript can often be unsatisfying. The language is not particularly expressive. (even after es8). Trying to created a pattern or structure often requires some awkward boilerplate, or some nasty looking code generously described as a hack. TypeScript offers a bit of relief. Obviously, at runtime the two languages are identical. However, during development TypeScript offers some compelling features that make writing code for js targets a bit more satisfying. Compile time type checking and a great toolchain (esp. With VSCode) make it an extremely attractive option. In this post I’ll be providing some advice and motivation for making a transition. I hope you’ll agree that there is no compelling reason to prefer JavaScript over TypeScript.

Adding More Guard Rails, Speeding up

Compiling with typescript is like using git branches. It is another layer of protection that allows developers to think about the abstract nature of code, rather than the blocking and tackling of development.

A major appeal of any dynamically typed scripting language is a low level of friction. The cycle of writing and running code in Node or on the browser feels very smooth and freeing. Adding a static type check can feel like an impediment. A simple counterexample to this perspective is misspelling a variable name. This can be annoying to figure out at runtime, especially across a call stack.

“ESLint can detect problems like this!” I hear you say. Quite right! ESLint is an excellent static analysis tool that has rules that can help detect problems before the code is run. I love using ESLint in my JS projects. I only wish that it could do more. I wish that ESLint could tell me if I’m trying to invoke on a token that is not a function, or if I’m referencing a key that doesn’t exist on a given type. I wish ESLint knew what types were at all. The tool that knows these things, and catches my errors is the TypeScript compiler.

It’s true if you are writing plain CommonJS modules that run in NodeJS directly, adding any precompile step is a non-zero effort. However, if you want that code to run on the client, you know are going to have to make some choices about how to be compatible with NodeJS and whatever browsers. Why not choose TypeScript. It’s the same amount of work to set up babel, but TypeScript adds in an additional level of checking.

Use the Best Tools (VSCode)

Using VSCode is a time saver. Sure, there are plugins available for most major editors, but VScode’s TypeScript facilities are peerless. Intellisense works well with JavaScript modules, but really shines in the context of typescript. I also really appreciate TSLint (ESLint for TypeScript) and the Vim plugin.

The Intellisense is especially satisfying when new types can be pulled in via the Definitely Typed project. This project has type declarations for lots of popular repos. This allows for compiled type checking and improved autocomplete in VSCode. I’ve found this especially useful when working with sinon and chai. I highly recommend checking this project out and using it to work with your dependencies.

Debugging node applications (run with the --inspect option) is also much better using VSCode than with Atom or the built in Chrome frame. The source maps load seamlessly and the debugging tools work seamlessly. It’s a bit of a pain to set up on the project (I’ve got a snippet that will help, but it’s worth the extra effort.

Developer can often be hesitant to try out new editors. My belief is that it is our responsibility to find the best tools for the job. Sentimentality doesn’t help write/read/debug code faster.

Take full advantage, but know the target

Try to take full advantage of Typescript. Use the access modifiers (public, private, protected). Use abstract to define desired inheritance patterns. Use interfaces and type aliases to express intention more concretely. But also understand that these concepts don’t exists at runtime.

For example, when you build typescript to JS modules client javascript modules can instantiate exported abstract classes. If there isn’t a compile time check there is nothing stopping JS from instantiating the prototypes. However, if you try to call an abstract method, you’ll get an exception because those methods won’t be included in compiled module at all.

I love the expressive power of these features, but it is crucial to understand the runtime implication, or lack thereof.

Conclusion

I’m still jealous of the runtime performance and advanced refactoring features. But typescript give me a taste of the latter, and I can enjoy a larger toolset for expressing ideas and modelling concepts.