TypeScript vs. JavaScript: Understand the differences

TypeScript transpiles to JavaScript and enables the development of large-scale applications

TypeScript vs. JavaScript: Understand the differences
Thinkstock

The world wide web basically runs on JavaScript, HTML, and CSS. Unfortunately, JavaScript lacks several features that would help developers use it for large-scale applications. Enter TypeScript.

What is JavaScript?

JavaScript started out as a scripting language for the Netscape Navigator web browser; Brendan Eich wrote the prototype over a period of 10 days in 1995. The name JavaScript is a nod to Sun Microsystem’s Java language, although the two languages are quite different, and the similarity in names has led to significant confusion over the years. JavaScript, which has evolved significantly, is now supported on all modern web browsers.

The introduction of client-side JavaScript in Netscape Navigator was quickly followed by the introduction of server-side JavaScript in web servers Netscape Enterprise Server and Microsoft IIS. Some 13 years later, Ryan Dahl introduced Node.js as an open source, cross-platform, JavaScript runtime environment independent of any browser or web server.

JavaScript language

JavaScript is a multi-paradigm language. It has curly-bracket syntax and semicolons, like the C family of languages. It has weak, dynamic typing and is either interpreted or (more often) just-in-time compiled. In general, JavaScript is single-threaded, although there is a Web Workers API that does multithreading, and there are events, asynchronous function calls, and callbacks.

JavaScript supports object-oriented programming using prototypes rather than the class inheritance used in C++, Java, and C#, although a class syntax was added to JavaScript ES6 in 2015. JavaScript also supports functional programming including closures, recursion, and lambdas (anonymous functions).

Prior to JavaScript ES6 the language had no tail call optimization; now it does, although you need to turn on strict mode ('use strict') to enable it, and the implementation varies from browser to browser. Strict mode also changes the semantics of JavaScript, and changes some normally silent errors to throw errors.

What’s with the “ES6” designation? The name for the standardized JavaScript language is ECMAScript (ES), after the ECMA International standards body; ES6 is also called ECMAScript 2015 (ES2015). ES2020 is currently a draft standard.

As a simple example to give you the flavor of the JavaScript language, here is some code to decide whether it’s day or evening and dynamically put the appropriate greeting into a named web element found in the browser’s document object:

var hour = new Date().getHours();
var greeting;
if (hour < 18) {
  greeting = "Good day";
} else {
  greeting = "Good evening";
}
document.getElementById("demo").innerHTML = greeting;

JavaScript ecosystem

There are numerous JavaScript APIs. Some are supplied by the browser, like the document API in the code shown above, and some are supplied by third parties. Some APIs apply to client-side usage, some to server-side usage, some to desktop usage, and some to more than one environment.

Browser APIs include the document object model (DOM) and browser object model (BOM), Geolocation, Canvas (graphics), WebGL (GPU-accelerated graphics), HTMLMediaElement (audio and video), and WebRTC (real-time communications).

Third-party APIs abound. Some are interfaces to full applications, such as Google Maps. Others are utilities that make JavaScript HTML5 and CSS programming easier, such as jQuery. Some, like Express, are application frameworks for specific purposes; for Express, the purpose is to build web and mobile application servers on Node.js. A number of other frameworks have been built on top of Express. In 2016, I discussed 22 JavaScript frameworks in an effort to make sense of what was becoming something of a zoo; many of these frameworks still exist in one form or another, but several have gone by the wayside.

There are many more JavaScript modules, over 300,000. To deal with that, we use package managers, such as npm, the default package manager for Node.js.

One alternative to npm is Yarn, which came from Facebook, and claims the advantage of deterministic installs. Similar tools include Bower (from Twitter), which manages front-end components rather than Node modules; Ender, which calls itself npm’s little sister; and jspm, which uses ES modules (the newer ECMA standard for modules), rather than CommonJS modules, the older de-facto standard supported by npm.

Webpack bundles JavaScript modules into static assets for the browser. Browserify allows developers to write Node.js-style modules that compile for use in the browser. Grunt is a file-oriented JavaScript task runner, and gulp is a streaming build system and JavaScript task runner. The choice between grunt and gulp isn’t decisive. I have both installed and use whichever one was set up for a given project.

To make JavaScript code more reliable in the absence of compilation, we use linters. The term comes from the C-language lint tool, which was a standard Unix utility. JavaScript linters include JSLint, JSHint, and ESLint. You can automate running linters after code changes using a task runner or your IDE. Again, the choice among the linters isn’t clear-cut, and I use whichever one was set up for a given project.

Speaking of editors and IDEs, I have reviewed 6 JavaScript IDEs and 10 JavaScript editors, most recently in 2019. My top choices were Sublime Text (very fast editor), Visual Studio Code (configurable editor/IDE), and WebStorm (IDE).

Transpilers allow you to translate some other languages such as CoffeeScript or TypeScript to JavaScript, and translate modern JavaScript (such as ES2015 code) to a basic JavaScript that runs in (almost) any browser. (All bets are off for early versions of Internet Explorer.) The most common transpiler for modern JavaScript is Babel.

What is TypeScript?

TypeScript is a typed superset of JavaScript that compiles to plain JavaScript (ES3 or higher; it’s configurable). The open source TypeScript command-line compiler can be installed as a Node.js package. TypeScript support comes with Visual Studio 2017 and Visual Studio 2019, Visual Studio Code, and WebStorm, and can be added to Sublime Text, Atom, Eclipse, Emacs, and Vim. The TypeScript compiler/transpiler tsc is written in TypeScript.

TypeScript adds optional types, classes, and modules to JavaScript, and supports tools for large-scale JavaScript applications for any browser, for any host, on any OS. Among many other wins for TypeScript, the popular Angular framework has been revamped in TypeScript.

Types enable JavaScript developers to use highly productive development tools and practices like static checking and code refactoring when developing JavaScript applications.

Types are optional, and type inference allows a few type annotations to make a big difference to the static verification of your code. Types let you define interfaces between software components and gain insights into the behavior of existing JavaScript libraries.

TypeScript offers support for the latest and evolving JavaScript features, including those from ECMAScript 2015 and future proposals, such as async functions and decorators, to help build robust components.

TypeScript language

The TypeScript language accepts JavaScript as valid, but offers the additional options of type annotations, type checking at compile time, classes, and modules. All of these are tremendously useful when you are trying to produce robust software. Plain JavaScript generates errors only at runtime, and then only if your program happens to reach a path with errors.

The TypeScript in 5 minutes tutorial makes the benefits clear. The starting point is pure JavaScript with a .ts extension:

function greeter(person) {
  return "Hello, " + person;
}
let user = "Jane User";
document.body.textContent = greeter(user);

If you compile this with tsc it will produce an identical file with a .js extension.

The tutorial has you change this code in a stepwise fashion, adding a type annotation person:string in the function definition, compiling, testing type checking by the compiler, adding an interface for a person type, and finally adding a class for Student. The final code is:

class Student {
    fullName: string;
    constructor(public firstName: string, public middleInitial: string,
        public lastName: string) {
            this.fullName = firstName + " " + middleInitial + " " + lastName;
    }
}
interface Person {
    firstName: string;
    lastName: string;
}
function greeter(person: Person) {
    return "Hello, " + person.firstName + " " + person.lastName;
}
let user = new Student("Jane", "M.", "User");
document.body.textContent = greeter(user);

When you compile this and look at the emitted JavaScript, you’ll see that classes in TypeScript are just a shorthand for the same prototype-based inheritance that is used in plain JavaScript ES3. Note that the properties person.firstName and person.lastName are automatically generated by the compiler when it sees their public attributes in the Student class constructor, and also carried over to the Person interface. One of the nicest benefits of type annotations in TypeScript is that they are recognized by the tools, such as Visual Studio Code:

typescript type annotations lg IDG

If there are errors in the code as you edit in VS Code, you’ll see the error messages in the Problems tab, for example the following if you delete the end of the line with the instantiation of Student:

typescript error lg IDG

The Migrating from JavaScript tutorial goes into detail about how to upgrade an existing JavaScript project. Skipping over the setup steps, the crux of the method is to rename your .js files to .ts one at a time. (If your file uses JSX, an extension used by React, you’ll need to rename it to .tsx rather than .ts.) Then tighten up the error checking and fix the errors.

Among other things, you’ll need to change module-based require() or define() statements to TypeScript import statements and add declaration files for any library modules you use. You should also rewrite your module exports using the TypeScript export statement. TypeScript supports CommonJS modules, like Node.js does.

If you get errors about the wrong number of arguments, you can write TypeScript function overload signatures. That’s an important feature JavaScript lacks. Finally, you should add types to your own functions, and where appropriate use interfaces or classes.

You usually don’t need to write your own declaration files for public domain JavaScript libraries. DefinitelyTyped is a repository of declaration files, all of which are accessible using npm. You can find the declarations using the TypeSearch page.

Once you have converted all of your JavaScript files to TypeScript, beefed up the types, and eliminated the errors, you’ll have a much more robust code base. Instead of constantly fixing the runtime errors reported by testers or users, you’ll be able to detect most common errors statically.

It’s worth watching Anders Hejlsberg discuss TypeScript. As you’ll hear from him, TypeScript is JavaScript that scales.

Copyright © 2020 IDG Communications, Inc.