Hot Take: TypeScript is the Best App Language

I hear your blood-curdling screams. I feel the searing heat coming from your intense loathing of JavaScript. I know why you hate it; trust me, I've been there. You're not wrong. JavaScript has many problems. But here's the thing — in terms of productivity, it doesn't matter because JavaScript gets the stuff that really matters right.

The Stuff That Really Matters

So, what really matters? Being able to build productively. Ultimately, our goal is to build things that work well quickly, and TypeScript is insanely productive.

TypeScript Makes the Nasty Parts of the Language Easy

The worst parts of JavaScript are nasty because they are unpredictable. Things that are ugly yet predictable are usually still easy to work with; problems typically arise because behaviors are difficult to comprehend and predict. Because TypeScript has an exceptional type system, it's easy to wrangle the worst parts of JavaScript into productive workflows.

Share Types on the Client and Server

In nearly every stack, the server and client are written in different languages, making sharing types between the client and the server impossible without an intermediate mapping (I have never seen this done in practice). In TypeScript, sharing types between the client and a Node.js server is trivial, which prevents many errors since it ensures that the shapes of requests and responses are shared. Furthermore, we can generate JSON validators directly from Typescript types, which makes validating the types of incoming JSON on the server easy and performant. Compare this to other solutions that account for incoming types in ad-hoc ways on both the client and the server, which often means treating everything as a string, which is error-prone and requires extra work.

TypeScript Has an Excellent Type System

Take this from someone who is a big fan of PureScript and Haskell, languages known for their elaborate and expressive type systems.

While things like type classes and effects don't have exact equivalents in Typescript, I have found that it provides suitable alternatives that prevent runtime errors just as effectively. For example, we can solve most of the same problems as Haskell's type classes in Typescript by passing parameters explicitly instead of implicitly through monads (if there's interest in this, then I'd be happy to write up my approach to this in another post). Asynchronous effect types become Promise<X> types instead, gaining most of the same benefits (linters rules can insist that promises are not implicitly discarded). Synchronous effects must be handled by convention (using expressive function names and separating effectful and pure code).

Performance

JavaScript is fast on modern engines. 'Nuff said. If it needs to be faster for your purposes, then you are either doing something peculiar or silly.

Language Features

JavaScript lacks some of the niceties of other programming languages. Still, many niceties are available through external libraries (lodash, underscore, ramda, etc.). Also, a surprising number of niceties exist in more recent JS standards. As for the language features that are still missing, I will gladly grab a language with straightforward deployments and a rock-solid type system that requires writing slightly more code. Writing code is cheap when the underlying behaviors are predictable. JavaScript has a versatile set of code reuse abstractions, meaning that most of what we're missing out on are nice syntaxes and built-ins that save you a few lines of code — it's not going to prevent you from keeping the overall design of your code clean or keep you from scaling your codebase.

Deployment Targets

JavaScript is the only language that can target all major platforms for application development. Moreover, we can reuse a lot of code while we're at it. Other languages have some cross-platform support, but JavaScript is the most ubiquitous and well-supported.

One big reason for this is that we don't have access to many native APIs or the ability to execute binaries directly from within a browser (things like WebAssembly exist, but they are not yet mature enough to rely upon for various reasons). Therefore, to target the web, we must either compile to JavaScript or use it directly. Solutions that involve compiling to JavaScript but attempting to layer a virtual runtime on top of it always involve significant performance hits and severe limitations on what you can do without writing involved bindings. Without a separate runtime, you end up writing code that looks like JavaScript but has a different syntax. Mentally, you have to constantly think about what the compiled JavaScript will look like, and you'll need to duck down to JavaScript to write bindings for anything that's not built-in. As WebAssembly support continues to improve, the status quo may change (I hope it does). But, for the time being, nothing competes with JavaScript.

TypeScript, on the other hand, is just JavaScript with types. That means that we can interface with the runtime and use dependencies directly. The vast majority of packages already ship with type definitions or have types available on DefinitelyTyped, so you don't have to waste time writing bindings, which is time-consuming and incurs significant performance costs at runtime.

Stay up to date

Get notified when I publish something new, and unsubscribe at any time.