What is TypeScript? Industrial-strength JavaScript

JavaScript is the language of the web, but it’s tough to manage for enterprise-scale development. TypeScript offers an attractive alternative

Historians who reflect on JavaScript’s emergence as a dominant programming language in the 21st century may find themselves quoting former Defense Secretary Donald Rumsfeld: “You go to war with the army you have, not the army you might wish to have.”

For growing numbers of programmers, JavaScript is the army we have. As we send it into the field to tackle ever more ambitious engagements both client and server side, we find ourselves battling the language itself.

JavaScript was never intended for large programs built by teams that use sophisticated tools to manage complex communication among internal modules and external libraries. Such teams have long preferred strongly typed languages like Java and C#. But those languages’ virtual machines never found a home in the browser. It was inevitable that JavaScript alternatives and enhancements would target the ubiquitous JavaScript virtual machine.

Among these is TypeScript, the brainchild of Anders Hejlsberg. The creator of Turbo Pascal and C# has created on behalf of Microsoft an important project that delivers useful benefits now and is moving forward nicely.

Getting started with TypeScript is easy

The TypeScript language is an optionally typed superset of JavaScript. Because existing JavaScript is valid TypeScript, you can begin using the TypeScript compiler—and TypeScript-aware tools—by simply changing filename extensions from .js to .ts.

That turns out to be a good way to start. TypeScript can be immediately useful, even before you add any type annotations to your own code, thanks to the type definitions collected at definitelytyped.org. The definitions available there, for hundreds of JavaScript libraries (including jQuery, Angular, and Bootstrap), describe the interfaces exported by those libraries. TypeScript-aware tools use those definitions to flow type awareness into TypeScript programs that import the annotated libraries.

That same kind of type awareness is also available, in a portable way, for JavaScript’s core library and for the browsers document object model (DOM). It’s portable because TypeScript is written in TypeScript, targets JavaScript VMs on any platform, and provides not only a compiler but also a set of language services that support code intelligence.

Visual Studio is one consumer of those services. Others include WebStorm, Eclipse, and Sublime Text. There’s also the TypeScript playground, an interactive webpage that completes statements, prompts for parameters and their types, and warns about type errors.

How to get started with TypeScript

The best way to adopt TypeScript is by approaching tasks in an incremental way: Start adding type annotations to otherwise unaltered JavaScript code. JavaScript’s implicit conversion among strings, numbers, and dates makes it hard to visualize the types of objects that flowed into and out of functions. Of course, you can document those expectations in comments, but without strong tool support, such documentation drifts away from the reality of the code.

With TypeScript, that documentation lives as part of the code during the development process, and enables an IDE—in my case Visual Studio—to bring the documentation to life. But it’s all a convenient illusion. When you compile to JavaScript, the annotations fall away. If you’ve done no more than add such annotations, the resulting JavaScript will exactly match your original unannotated code. (If you have restructured the code to use classes and modules, you can use the JavaScript source map the compiler emits for TypeScript-source-level debugging in browsers and stand-alone debuggers.)

“Instead of having a switch that turns types on and off,” says Hejlsberg, “we have a dial.” You can invest incremental effort for incremental reward. As you add annotations, you improve your own ability to reason about the code, and you enable tools to provide more powerful automated support for that reasoning.

There’s also a multiplier effect because the TypeScript compiler works hard to infer types where it can. If you tell it that a function returns a string, it will know that a variable holding the result of that function is a string, even if you haven’t annotated the variable with the string type. If you later try to assign it a number, the compiler will complain.

In my initial TypeScript work, once I’d checked all my primitive types I began writing simple classes to model JavaScript objects made from primitive types. As I added type annotations for these compound objects, Visual Studio made their members available for code completion. Functions that received—or returned—those objects as parameters became self-describing and self-checking. Again this code awareness flowed through the program. In JavaScript you can’t know, a priori, whether the return value you’re assigning to a variable is of the type you expect. That uncertainty results in much confusion and error. Why tolerate it?

TypeScript has a (software) test for that

The growing popularity of dynamically typed languages like JavaScript happened to coincide with a growing appreciation for the practice of writing self-testing software. As a result, there’s been some tendency to conflate the two trends. If your test coverage is sufficiently robust, some say, there’s no reason to incur the overhead of static types.

We can debate the feasibility of writing tests that obviate the need for static typing. But if you buy the argument that type safety becomes important at large scale, TypeScript invites you to consider how you want to invest your testing effort.

Tests are, after all, another kind of overhead: more code to write and maintain. Arguably what can be tested mechanically, should be. If computers can figure out whether two software objects are compatible, people ought to delegate that job to them. Writing tests, like writing the software those tests exercise, is a creative act that should require and benefit from human intelligence. Automatic type checking is, from this point of view, a way to free up human creativity for higher purposes. 

One of those higher purposes is effective naming. “There are only two hard things in computer science,” it’s famously said, “cache invalidation and naming things.” The names of variables, functions, classes, and modules are the most fundamental kind of documentation.

It’s hard enough to coin names that usefully describe their roles in a program. It’s even harder to adjust those names as those roles evolve during the ongoing development of a program. That’s partly because naming anything well—in software or in any other domain—is intrinsically hard. But in software there are special risks when names that appear in different contexts, and mean different things, can’t easily be recognized as such. That’s the situation in JavaScript. There are a number of incompatible workarounds for organizing code into modules, but the language itself doesn’t support any of them.

ECMAScript 6 took a major step forward. It provided a standard way to organize programs, which may spread across many files, as sets of modules. That mechanism, which TypeScript adopted, is a boon for large-scale development. When module dependencies are declared in a standard way, programmers can more readily understand those dependencies, tools can automate that understanding, and code refactoring becomes less risky.

For example, in a conventional JavaScript environment it’s risky to rename a module-scoped or class-scoped variable, function, or class that’s referenced in many places, perhaps from many files, because there’s no way to know if the name is intended to mean something else in another context. A TypeScript-aware IDE knows and can make such refactoring a safe operation.

Join the newsletter!

Error: Please check your email address.

More about EclipseMicrosoft

Show Comments
[]