JavaScript is far from the best-designed language and has a number of features and quirks that make it difficult to build large-scale applications:

It’s easy to define a variable in a global namespace in JavaScript

The equality operator (“==”) type coercion rules are confusing and error-prone

“This” keyword can refer to a different object when used inside a function depending upon the way that function was invoked

These are just a few of the examples of standard JavaScript behaviors that can be confusing and error-ridden code.

NOTE: Douglas Crockford’s book “JavaScript: The Good Parts” is surprisingly slim, but over the course of 150 pages it highlights not only the good, but also the bad and the worst features that developers need to be aware of when writing in JavaScript.

Moreover, JavaScript being an object-oriented language, does not feature a class-based inheritance, opting out for a prototypical one. While the prototypical inheritance is certainly not a bad thing in itself, it’s less familiar to the developers originating from a class-based language background like Java or C#. It makes it a bit difficult to figure out how to organize a hierarchy or hide some object members like private fields and methods.

That said, when it comes to a web application, client-side development JavaScript is the only option. It’s the only client side language universally supported by all browsers.

With the increased focus on web applications as a distribution model that is very close to fulfilling the promise of “write once, run everywhere” the size and complexity of the modern client-side web apps have noticeably outgrown the capabilities of JavaScript. It is highly unlikely that JavaScript will ever be replaced (at least in the near future) by a better suited language, because it would be a significant challenge to convince all of the browser vendors to universally agree on and adopt a new language. This realization has prompted different groups to develop solutions that compile existing or entirely new languages with a JavaScript code instead of being used on their own. Among these are:

Google’s GWT and Dart

CoffeeScript

Microsoft’s TypeScript

NOTE: In a way, those solutions treat JavaScript as an assembly language for the web.

TypeScript Overview

This overview focuses on TypeScript and tries to evaluate what it can offer to help with client-side application development on the web.

TypeScript is described as a strict super-set of JavaScript, which adds optional static typing and class-based object-oriented programming aligned with ECMAScript 6 standard proposal. It is compiled down to an idiomatic JavaScript and does not require any kind of runtime library to support it.

This description embodies two of the first TypeScript design goals, which are:

To statically identify constructs that are likely to be errors To provide a structuring mechanism for larger pieces of code

Type System

By introducing an optional type system, TypeScript tries to bring the benefits of statically typed languages to the dynamic world of JavaScript. Those benefits include:

Making it easier to write correct and predictable code, eliminating common mistakes like typos or incorrect assumptions about a type of value. These mistakes are caught at compile time by a type checker, as opposed to dynamic languages, which typically require writing more unit tests to cover these cases. That does not mean that an application written in TypeScript does not require unit tests, though. It just means that it needs fewer of them.

With the knowledge about types an IDE can be much more helpful, providing features like autocomplete. This makes APIs more discoverable, freeing a developer from the necessity of checking with the API reference all the time or knowing all of the API details by heart. This often brings a significant productivity boost.

It makes it possible to perform automatic and safe code refactoring like renaming a function or a variable. Such tooling support, along with the ability to navigate the code more easily (with features like “go to definition” and “find all references”) makes it easier for developers to work with a large codebase and keep it in a maintainable state.

The type system that TypeScript introduces is flexible enough to express the majority of widely used JavaScript patterns. One of the biggest factors that differentiate this type system from those featured in mainstream, strictly typed languages like Java or C# is that it implements “structural subtyping,” which is also known as “duck typing.” More details on the topic are available here. Other notable features include type inference and generics support.

Classes

When it comes to structuring mechanisms, TypeScript introduces the notion of class-based object-oriented programming. This makes it fairly simple to structure code into classes, create class hierarchies, and manage the scope and visibility of methods and data throughout the system.

TypeScript classes can have private fields and methods. Private/public member visibility is enforced by the compiler at the compile time. However, in the compiled JavaScript code private members are publicly accessible.

It’s possible to completely hide methods and variables in JavaScript by putting them inside a constructor function and making them accessible to the class internals through a closure. But closing over privates takes up more memory and is not recommended from a performance perspective, which is why the TypeScript team opted in for type system level visibility checks only.

NOTE: Anthony van der Hoorn’s presentation “Front End Design Patterns: SOLID CSS + JS for Backend Developers” gives a few good examples of how to create private variables and methods in JavaScript.

Modules

TypeScript also uses the concept of modules, internal and external.

In JavaScript every variable or function that is not defined in a function is automatically created in a global scope (window object in browsers). Variables defined with no “var” keyword are also created globally. Polluting the global namespace may cause naming conflicts, in which a variable or function in the global scope can override an existing one with the same name and hence create really hard-to-find issues. This is especially easy to do by mistake with any non-trivial code base and/or teams with multiple developers.

TypeScript’s internal modules are similar to namespaces (like the ones available in C#) and allow defining symbols (variables, functions, classes) off of the global scope. Internal module declaration can be split into multiple files. This encourages a development team to have a well structured project with small single-purpose files.

External modules are TypeScript’s way of defining asynchronously loaded modules for either node.js (commonjs) or require.js (AMD) module-loading systems.

TypeScript also uses modules to declare the API that third-party libraries expose. It’s done through so called “ambient internal or external modules,” which contain only declarations and interfaces, but no implementation. This way it’s possible to provide the same level of IDE / type checker support for code that is available in pure JavaScript as if it was written in TypeScript. Such ambient declarations are stored in “.d.ts” files.

There is a constantly growing repository of ambient module definitions for popular JavaScript libraries called DefinetelyTyped.

TypeScript Pros

Here are the strong points of TypeScript:

Rich IDE support with autocomplete and code navigation features

Safe automatic refactorings

Discoverable APIs and more self-explanatory code contracts (through type declarations)

Typos caught at compilation time

Class-based OO, with inheritance, private members and interfaces

Module support

Existing JavaScript code interoperability:

Compiles down to idiomatic JavaScript Any JavaScript code is a valid TypeScript code

Relatively low risk of trying TypeScript out:

You can start with JavaScript code and gradually add type annotations Should you want to stop using TypeScript, you can compile it to JavaScript and use it as a new codebase and continue writing JavaScript code

Those benefits are a great help when dealing with a mid- to large-sized codebase and a team of developers working on a project. TypeScript brings the development experience closer to what Java and C# developers have come to expect from their respective platforms in terms of productivity and maintainability.

TypeScript Cons

However, everything comes at a price, and TypeScript is no exception. Here are some things that could be considered drawbacks or hurdles compared to JavaScript:

In order to get the most out of the TypeScript, developers will need to use the type annotations everywhere in their code, which might at times be cumbersome (or perceived as such coming from a dynamic language background). Some effort might be required to write a strongly-typed version of the JavaScript code.

TypeScript’s type system, while being more flexible than what is commonly found in mainstream strongly-typed languages, is still not as flexible as JavaScript itself (which to some extent is the point of TypeScript — imposing a strong typing system to make it easier to reason about JavaScript code which is inherently dynamic). Certain commonly used JavaScript patterns, like mixins, can be difficult or clunky to use in TypeScript, though the TS team is working on making those smoother with each release.

Quality of ambient module definitions (“.d.ts” files for third-party libraries) sometimes leave much to be desired. Certain definitions might be too specific or not specific enough, making you jump through hoops trying to make the type checker happy (adding some type-casing statements or worse, casting type to “any” which basically defeats the purpose of TypeScript’s strong typing in those instances).

Not all third-party libraries have ambient definitions available. The development team will have to write and maintain their own if they want to use a particular library and take advantage of TypeScript.

In order to run the application in the browser, a compile step is required to transform TypeScript into JavaScript.

Since any JavaScript code is a valid TypeScript code, TypeScript can not fix some of the fundamental JavaScript issues. On the other hand, it can create an illusion that it does: it could cause even more confusion by giving the illusion of safety.

All in all, none of these is a deal-breaker. For medium- to large-sized projects, what TypeScript brings to the table far outweighs the downsides. It’s not a silver bullet that solves all of the problems of JavaScript, nor is it a total replacement for JavaScript. To use TypeScript and get the most out of it, the development team needs to be familiar with both. It is a development tool that can help to structure and manage a large codebase.

NOTE: In his recent appearance at NDC 2014, Steve Sanderson (a Microsoft employee working on the new Azure management portal; creator of KnockoutJS — a popular JavaScript MVVM framework) shared his team experience of building a large scale web application using TypeScript. The recording of his presentation is here.

Programming Tips

Here are some tips that can make TypeScript programming easier:

Get to know what JavaScript code TypeScript generates for each of the constructs like classes, modules, enums etc. This will make it much easier to figure out how to use TypeScript with frameworks like AngularJS or js. It’s easy to learn that using an online TypeScript playground (http://www.typescriptlang.org/Playground), which dynamically compiles TypeScript code entered in a browser window and shows it side by side with the generated JavaScript code.

Do use type declarations. The effort required to avoid using “any” will pay off at the time of refactoring. TypeScript handbook is a good source of information. Here are some useful sections:

Be prepared to write or adapt existing ambient module declarations.Following links provides guidance and best practices:

Remember that TypeScript is still a JavaScript at it’s core:

Know what ‘this’ means. ‘this’ in arrow functions (lambda expressions) is lexically bound: what ‘this’ inside of the arrow function means is determined by where the function is defined and not where it is used. Functions defined with ‘function’ keyword and methods on TypeScript classes follow normal JavaScript rules, so what ‘this’ means depends on how the function/method was invoked.

Always use “===” instead of “==”. As this article points out, although TypeScript compiler will raise an error if you attempt to compare values of different types, should any of the compared values be of type “any” no error is generated and JavaScript comparison rules (often confusing and counter-intuitive) will kick in.

Be aware of automatic semicolon insertion. For example, always write return value in the “return” statement on the same line as the “return” keyword. Otherwise, the JavaScript interpreter will automatically insert the “missing” semicolon and the function return value will be “undefined.”

The TypeScript compiler will actually insert missing semicolumns in the JavaScript code generated to make it easier to find such mistakes.

Read “JavaScript: The Good Parts” by Douglas Crockford and avoid using the bad and worst parts of JavaScript in your TypeScript code. JavaScript Garden is another place where the quirky parts of JavaScript are collected and documented.

Avoid common pitfalls connected with modules:

Use the “export = ClassName” construct when declaring an external module with a single class or function. It’ll avoid needless namespacing in the code that uses the module. Remember that paths to external modules are case-sensitive. Use “amd-dependency” tag (using “/// <amd-dependency path=’…’ />” syntax) instead of “import” keyword to declare a dependency on a non-code module when using RequireJS plugins like “text!”. The “Loader plugins” section of this article provides more details on that topic.

Consider having a single “references.ts” file with all the references to internal modules and ambient declarations. Add a reference to it at the beginning of each TypeScript file (using “/// <reference path=’…’ />” syntax). Reference tags are always relative to the file they are declared in, so this approach can make it easier to write and reorganize files in a project, compared to a situation when each file references all ambient and internal modules it uses directly.

Link both TypeScript and JavaScript code. Integrate static checker tools into the build procedure, pay attention to warnings (you may even want to fail the build if any warnings are generated). Here are some helpful tools:

You can use Grunt or Gulp to compile, lint, concate and minimize TypeScript code. Both have ability to watch and recompile TypeScript files as soon as they are changed.