A decision was made, and I ended up with two years+ worth of TypeScript experience.
TLDR: It took me a long time to warm up to it, but TypeScript turned out to be a great tool, jump to the conclusion to find out more.
What is TypeScript?
TypeScript give you Ecma 6 features today. It’s a transpiler that takes Ecma 6 syntax and various additional TypeScript features and turns them into valid Ecma 5 or Ecma 3 code. The good people over at Microsoft have been working very hard on achieving this and have done a fantastic job so far. On top of that you get something that is not present in Ecma 6, type safety. Type safety is of course a very contentious issue. I’d be surprised if the average programmer hasn’t been witness to, or been part of, the great debate of static versus dynamic typing. I never really had strong feelings either way, I just had a general unease with the clunkiness of Java’s type system versus the elegance and succinctness of languages such as Python and Ruby. To be honest I think most of the debate around static versus dynamic is probably Java’s fault, a view I’ve seen kicking for a while.
The genius of TypeScript is the fact that the type system is optional. You can happily use TypeScript for its other features, such as; its class syntax, the module system and other types of inferred type checking without ever breaking out explicit: “number”, “boolean”, “string”, “Object”, “Event”, “any”…etc
The Good Parts
That Sweet Sugary Syntax
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38
I’m not going to go into a full feature list. But as you can see the above style of syntax is the kind of thing you’d expect from an OOP language. One of the main developers of the language was Anders Hejlsberg, who is the chief designer of the C# development language and .NET framework. So it coming from an excellent pedagogy.
As you can see from the code snippet, there is a class syntax which is cleaner and more recognizably traditional OOP development languages like C# and Java. Under the hood the code that is generated is essentially the usual prototypical inheritance system you’d expect.
Naturally for an OOP language it has interfaces, public/private methods and class attributes.
Finally, for me one of the great features of TypeScript is its syntax, for the most part, does what you’d expect. You don’t need to worry about the context of “this” so much. Using the “fat arrow” Ecma 6 syntax (which original came from CoffeeScript) you can get around a lot of messy closure issues.
1 2 3 4 5 6
Imagine a world where you didn’t need to check every parameter into a method for “nil” state? Great isn’t it? Well with a complication step you express your desire for a method to never be called without some parameter and then the compile makes it so.
Now that isn’t the whole story, but let me save that for the Bad Parts below.
As I mentioned above, TypeScript gives you a few more syntax tools to allow you to express your intentions. As such you have a better idea about the intent of a method beyond just the name, you get types to go along with the input parameter names. You can see at a glance whether there is a return value and what type that is going to be. The pesky open ended “options” object now had a proper description of the keys and values and expect types of all of their possible contents. All very useful things. And of course the tools (that are really only showing up in the last year or so) allows you to generate decent dependency graphs…etc so you can plan refactors and see hotspots pretty quickly.
Since there is a much richer source code to draw from documentation generation should becomes a lot easier. In the case of JSDocs, you have to keep those comment sections in sync with the code section and since there is nothing to properly enforce that relationship things go out of sync quickly and the comment become a lie. Ultimately I didn’t find any decent documentation generation tools for TypeScript, I tried getting a TSDoc style system up and running but with little success.
The Bad Parts
As yes, and after all that positively comes the pain.
Tooling took a long time
The primary reason we went with TypeScript was “tooling” which is hilarious when you consider we took it up when it was [v0.8]. The only tooling that existed was Visual Studio, which at the time we couldn’t get to work for us. At that stage Visual Studio and TypeScript needed to be taken up at the same time, we had begun writing TypeScript code before then. We didn’t have a huge code base but it was big enough for someone to say, screw it, when it didn’t instantly work. When I was leaving the team was looking at Webstorm which looks like a fantastic tool and would be a major boon to anyone looking at development environments for any kind of web development.
The reason VS didn’t work for us was because of the “reference” paths. For the compiler to be able stitch things together properly you’d have to include a reference to the paths of other files that it needed (later we found the “reference file” method to be easier and more maintainable). For some reason I think that screwed with VS and it probably didn’t help that we had separate modules going and we weren’t using VS to build our code.
Obviously though, that was not the languages fault.
Source maps, though they sound like a nice idea are a nightmare. The technology was made in hell and deserves to burn there, forever.
Hyperbola out of the way, the experience in Chrome, which was the main browser I was debugging in, was terrible. Not initially though, initially it was great. Even having the Sublime Text style “Ctrl+P” open a file with fuzzy search was brilliant. The problem was when you placed breakpoints and subsequently edited your code. This is where things went to hell. I never quite figure out the set of steps to reproduce it (to be honest I turn off Sourcemaps pretty quickly) but when you had break points and were refreshing the page after doing an edit you could find yourself debugging code out of sync with the new edits. Sometimes I found I was looking at a piece of code that wasn’t the code I thought I was debugging and wondering what in the hell was going on, why were some things nil when they should have a value. All manner of strange things would happen with Sourcemaps and me (other develops never seem to run into issues) so I had to give up on them.
Obviously though, that was not the languages fault.
Inheritance and third party libraries issues
MaybeTyped and probably okay
You know that thing I said about “you need to know a library inside out before being able to use it” and how TypeScript/DefinitelyTyped solved that, yeah only kinda. First off the bat, not all the libraries are there. Lot of them are, I grant you that, but not all of the ones you might need (for instance there is lots of Angular and very little Backbone). If you need a library that is not a part of that repo, then tough luck, go write your own. Which if you have to do that and are under time constraints can be kind of tough.
There are a few issues in a real production application that you have to account for when using a library. You want to make sure not to be too quick to update libraries, sometimes things change pretty radically (it’s usually okay if they use semantic versioning). That radically different stuff might mean you can’t upgrade right now. That’s an issue.
Sometimes DefinitelyTyped definitions can be written badly. It’s just a fact of life that a piece of code can be written badly, this is a community project after all. These people aren’t doing this for money, it’s on their spare time. With that in mind it makes sense to keep up to date with a d.ts because I’ve seen bugs from a d.ts having an “any” where it should have a more solid type and engineers wasted a bunch of time wondering why in the hell the compiler didn’t catch something.
Sometimes you can’t move up compiler version, that can be an issue if someone in the community re-write huge swatches of a d.ts to use some new compiler feature (which is the correct thing to do in my view).
So you have a few vectors… the third party library version can increment, the d.ts version can increment at its own pace as well, the compiler version can increment too. Frankly it was a nightmare to manage and keep an eye on it. What would be needed would be a package management system for those DefinitelyTyped files, which does exist now, but that is still in its early days. Frankly it would of been better for the TypeScript team to weight in on the design and production of those kind of support systems because it was sorely lacking during my time with the language.
Having a build system was a problem, for reason you’d think of and reasons you wouldn’t.
Now frankly that could of been our fault. We may have not been taken full advantage of the module system, or were using it incorrectly. When I was there we had three modules with about 30kloc~. Not massive when you consider TouchDevelop looks to be in or around 160kloc~. I’m not sure what we were doing wrong but I found development to be slower even then what I had experienced with Java.
The other issue with that was we had to re-compile our whole code base when any file (TypeScript) changed. I reckon if the compiler allowed you to compile individual files yet somehow was still able to figure out how to stitch them together without having to consume and read the whole code base again, that would produce an enormous build performance improvement. But that is just speculation. I’m sure the builds could be acceptable if it we had the right type of modules/build setup.
When the build fails
My single biggest issue with TypeScript is that it produces artifacts even when the compiler booms out and blows up with errors about your crappy (mine in this case) source code. The worst thing was since we had this kind of cascading build system (we used rake and task dependencies) it would find these artefact’s on a second build and then just assume everything was find. That I see as an genuine issue with the compiler. But I’m sure it’s probably a feature not a bug.
I found there was a great deal of verbosity in the code I was writing. I don’t blame the language for that, more and more features appeared (even long before I left my last job) that was going to make the code we had written far simpler, it just we didn’t have time to look at upgrading the compiler right then.
Ultimately the root cause to all of my pain with TypeScript was that we adopted it too early. It was too early for good tools, any tools. It was too early for the community, it was too early to find out what would be the best set of “best practices”. We spent a lot of time wrangling with technology instead of writing the tool we were building. But that is the nature of being on the bleeding edge and having gone through that pain the project was in a better place and on a better trajectory as a result.