TypeScript
Last week was the 5th anniversary of the launch of TypeScript, and I had the opportunity to go back to visit Anders and the team for the weekly TypeScript design meeting — to congratulate them on the milestone and on the incredible progress they’ve continued to make with TypeScript over the 4 years since I left the team.
I also took the opportunity to dig up some old demos I had done of TypeScript from the earliest days of our work on TypeScript (then codenamed Strada) to share with the team.
I started working on a project to help application-scale development teams manage large JavaScript codebases in the Fall of 2010 along with Steve Lucco. Steve had been talking to a few engineering VPs in Office who highlighted the rapid transition of their development teams from C++/C# over to JavaScript as they invested in web-based applications and experiences. These teams missed the productivity tools and confidence that the type systems of C++ and C# and the Visual Studio IDE had provided — and they wanted to know what we could do to help as they moved to JavaScript. At the same time we saw the broader industry trend in a similar direction as fast JavaScript engines, progress on HTML5, and several impressive large web “applications” had all converged to quickly change the way JavaScript was being used on the web over the previous couple of years.
There were many options already available, but none seemed to be resonating well with a broad enough section of the market. Internally at Microsoft, Script# was being used by some large teams. It let them use C# directly instead of JavaScript, but as a result, suffered from the kind of impedance mismatch you get when trying to stand at arms length from the runtime model you are really programming against. And there was Google’s Closure Compiler, which offered a rich type system embedded in comments inside JavaScript code to guide some advanced minification processes (and along the way, caught and reported type-related errors). And finally, this was the timeframe of a rapid ascendancy of CoffeeScript within the JavaScript ecosystem — becoming the first heavily used transpiled-to-JavaScript language and paving the way for transpilers in the JavaScript development workflow. (Aside — I often explained TypeScript in the early days using an analogy “CoffeeScript : TypeScript :: Ruby : C#/Java/C++”, often adding — “and there are 50x more C#/Java/C++ developers than Ruby developers :-)”)
What we quickly discovered we wanted to offer was a “best of all worlds” at the intersection of these three — a language as close as possible to JavaScript semantics (like CoffeeScript) and syntax (like Closure Compiler) but able to offer typechecking and rich tooling (like Script#).
We started working on it as a side-project in the Fall and Winter of 2010. As a forcing function to help ensure we had something to show, we signed up to do a presentation internally on what we were working on at a mini-conference with programming languages folks from both research and product teams across Microsoft. However, in the month before the talk, Steve (the only engineer on the project at the time!) had a wrist injury which prevented him from doing any serious coding. So, not wanting to back out of the talk — I decided to hack something together that would allow us to get across the experience we were trying to deliver — even without having an actually working compiler. (Steve did shortly after build the first real TypeScript compiler, which enabled our first internal customer — the team that was building what would later become VS Code — to start using TypeScript for real).
Here’s the main piece of code we used in the first Strada demo outside our immediate team — on February 1st, 2011.
There are a number of elements of what ultimately became TypeScript that stand out here.
- Implicit flow of types through the code on lines 28–30 without the need for annotations.
- Classes as an important way to be able to express both the implementation and type of the class at the same time — classes are handy in plain JavaScript, but even more valuable in TypeScript where you want to describe both the implementation and type and would prefer not to have to say everything twice.
extern
to be able to seamlessly interoperate with plain-old-JavaScript code and libraries — even when they didn’t (yet) have types associated with them.
But there are also several things you can see here that we ultimately moved away from with TypeScript.
- The more “functional” class syntax shown here was something we really wanted to enable — I had just come from working on F# which used a similar short-hand syntax for classes, and we’d also been discussing a syntax along these lines for C# around the same time. It also fit well with the at-the-time popular style of representing state in JavaScript “classes” as closure-captured state — instead of as object-properties as you see in the protoype-based approach to classes that has since become standardized in ECMAScript. This was a contentious topic for most of the time we worked on TypeScript prior to it’s public launch — the in-progress work on ECMAScript classes was heading in a different direction, and though we wanted to try to align with future standards, we weren’t in love with the way you would have to write TypeScript classes on top of the standards approach (saying the name of the variable four times in common cases). We ended up prioritizing standards alignment — which at the time was a risky bet — it wasn’t a-priori clear that ES6 would end up any better than ES4 (which never shipped) — but in retrospect was a key enabler of TypeScript’s promise to remain as thin as possible of a layer over JavaScript even as it evolved from ES3 to ES5 to ECMAScript2017 and beyond.
- Types in prefix position — you can see that types are specified above as
string name
instead ofname: string
as they are now in TypeScript. We initially gravitated to that syntax because we used the namevar
for the type that is now calledany
, allowing the implicit typing toany
to be effectively explicit in a statement likevar x = ...
. This was also similar to the work done not long before in C# whenvar
was introduced there. But putting a type in this position in the grammar leads to a number of parsing issues, and there was good precedent for JavaScript-like languages which used thename: string
format (most notably — ActionScript and the defunct ECMAScript4). This one changed over quite early — code samples I have from just a couple months later already use the:
syntax. - We had the C#-style
class Foo : Bar
syntax for super classes — which we later transitioned over to useextends
. - Somewhat more subtly — note that the code is in a
<script type="text/strada">
block — and the comment noting thatStradaCompiler.js
is responsible for compilingtext/strada
blocks on the fly. We actually imagined at the time that you would provide TypeScript code directly in your HTML via<script>
tags — instead of having a separate build step — and it would get transpiled on page-load (and hopefully cached). This was a somewhat popular technique for small-scale CoffeeScript projects at the time, but as we talked to more larger development teams we found this was not practical for many/most production workflows. In practice, getting this kind of tight development workflow experience ended up being solved by tools like WebPack and similar running watchers that dynamically recompile code but still present it to the browser in a compiled form.
But even more fun is how we made that piece of code actually work.
I had a little side project at the time to write a JavaScript interpreter in F# (as a cathartic tool in my transition from working on F# to working on JavaScript), and part of that was a fairly solid JavaScript (ES5) parser. I made some (surprisingly minor) modifications to the grammar to support the new constructs we were adding with Strada — mostly along the lines of this:
I hacked into the pretty-printer a way to emit the resulting ASTs as plain JavaScipt — but instead of throwing away the types, I converted them into into Closure Compiler comments. Now, there’s more or less no way to make this a robust process — but it was enough for the demo!
From there — you guessed it — we just invoked the Closure Compiler on the resulting code as a “type checker”. Closure Compiler had (and still has) a convenient hosted service on AppEngine, so we just POSTed the pretty-printed code to that service to get errors.
These were also the days of Silverlight (and Flash) still playing a meaningful role on the client side. And so to run the parser (implemented in F#), we hosted it as Silverlight inside the page — which reached out to find text/strada
scripts, parse them, send them to Closure and report the errors.
The result was that we could get live error reporting as we changed the contents of the <script>
tag inside the developer tools, giving the impression that there was just a background typechecker running. This managed to demo some key parts of the experience we were trying to enable — even though in practice the way were were showing it couldn’t possibly scale to even a single real user 😄.
Here’s one more of the code snippets we demoed. You can see a few more interesting differences from what became TypeScript here — double
as a type instead of number
(we imagined we might add int
as well — but that took us away from JavaScript semantics) and the I
prefix on interfaces as a C#-inspired convention — along with some things that stayed, like interfaces and the ability to seamlessly mix untyped object lookups with strongly typed classes.
A little over a year and a half after that first demo, we launched TypeScript. And in the 5 years since, TypeScript has become one of the fastest growing programming languages across the software industry.
Watching now from the outside, I’m constantly impressed with the TypeScript team’s ability to both stay true to it’s roots of being a thin layer on top of JavaScript, while at the same time evolving to meet the challenges of being a typesystem that can in practice be productively used across almost any JavaScript codebase. As new frameworks and development styles have emerged in the JavaScript ecosystem, TypeScript has managed to evolve as well, while still staying focused and approachable.
Congrats to everyone on the team and in the TypeScript community for the incredible work making this happen!