JavaScript - TypeScript: Making .NET Developers Comfortable with JavaScript
By Shayne Boyer | January 2013. The growth and capability of JavaScript applications is huge. Node.js, an entire platform for developing scalable JavaScript applications, has become enormously popular, and it’s even deployable on Azure. Moreover, JavaScript can be used with HTML5 for game development, mobile applications and even Windows Store apps.
As a .NET developer, you can’t ignore the capabilities of JavaScript, nor its spread in the marketplace. When I make this statement to colleagues, I often hear groans about how JavaScript is hard to work with, there’s no strong typing, no class structures. I combat such arguments by responding that JavaScript is a functional language and there are patterns for accomplishing what you want.
This is where TypeScript comes in. TypeScript isn’t a new language. It’s a superset of JavaScript—a powerful, typed superset, which means that all JavaScript is valid TypeScript, and what is produced by the compiler is JavaScript. TypeScript is an open source project, and all of the information related to the project can be found at typescriptlang.org. At the time of this writing, TypeScript was in preview version 0.8.1.
In this article, I’ll cover the basic concepts of TypeScript in the form of classes, modules and types, to show how a .NET developer can become more comfortable with a JavaScript project.
Classes
If you work with languages such as C# or Visual Basic .NET, classes are a familiar concept to you. In JavaScript, classes and inheritance are accomplished through patterns such as closures and prototypes. TypeScript introduces the classical type syntax you’re used to and the compiler produces the JavaScript that accomplishes the intent. Take the following JavaScript snippet:
This seems simple and straightforward. However, .NET developers have been hesitant to really get into JavaScript due to its loose approach to object definition. The car object can have additional properties added later without enforcement and without knowing what data type each represents, and thus throw exceptions during runtime. How does the TypeScript class model definition change this, and how do we inherit and extend car? Consider the example in Figure 1.
Figure 1 Objects in TypeScript and JavaScript
On the left is a nicely defined class object called car, with the properties wheels and doors. On the right, the JavaScript produced by the TypeScript compiler is almost the same. The only difference is the Auto variable.
In the TypeScript editor, you can’t add an additional property without getting a warning. You can’t simply start by using a statement such as car.trunk = 1. The compiler will complain, “No trunk property exists on Auto,” which is a godsend to anyone who has ever had to track down this gotcha because of the flexibility of JavaScript—or, depending on your perspective, the “laziness” of JavaScript.
Constructors, though available in JavaScript, are enhanced with the TypeScript tooling again by enforcing the creation of the object during compile time, and not allowing the object to be created without passing in the proper elements and types in the call.
Not only can you add the constructor to the class, but you can make the parameters optional, set a default value or shortcut the property declaration. Let’s look at three examples that show just how powerful TypeScript can be.
Figure 2 shows the first example, a simple constructor in which the class is initialized by passing in the wheels and doors parameters (represented here by w and d). The produced JavaScript (on the right) is almost equivalent, but as the dynamics and needs of your application expand, that won’t always be the case.
Figure 2 A Simple Constructor
In Figure 3, I’ve modified the code in Figure 2, defaulting the wheels parameter (w) to 4 and making the doors parameter (d) optional by inserting a question mark to the right of it. Notice, as in the previous example, that the pattern of setting the instance property to the arguments is a common practice that uses the “this” keyword.
Figure 3 A Simple Constructor, Modified
Here’s a feature I’d love to see in the .NET languages: being able to simply add the public keyword before the parameter name in the constructor to declare the property on the class. The private keyword is available and accomplishes the same auto declaration, but hides the property of the class.
Default values, optional parameters and type annotations are extended with the TypeScript auto property declaration feature, making it a nice shortcut—and making you more productive. Compare the script in Figure 4, and you can see the differences in complexity start to surface.
Figure 4 The Auto Declaration Feature
Classes in TypeScript also provide inheritance. Staying with the Auto example, you can create a Motorcycle class that extends the initial class. In Figure 5, I also add drive and stop functions to the base class. Adding the Motorcycle class—which inherits from Auto and sets the appropriate properties for doors and wheels—is accomplished with a few lines of code in TypeScript.
One important thing to mention here is that, at the top of the compiler-produced JavaScript, you’ll see a small function called “ ___extends,” as shown in Figure 6, which is the only code ever injected into the resulting JavaScript. This is a helper class that assists in the inheritance functionality. As a side note, this helper function has the exact same signature regardless of the source, so if you’re organizing your JavaScript in multiple files and use a utility such as SquishIt or Web Essentials to combine your scripts, you might get an error depending on how the utility rectifies duplicated functions.
var __extends = this.__extends || function (d, b) { function __() { this.constructor = d; } __.prototype = b.prototype; d.prototype = new __(); } var Auto = (function () { function Auto(mph, wheels, doors) { if (typeof mph === "undefined") { mph = 0; } if (typeof wheels === "undefined") { wheels = 4; } this.mph = mph; this.wheels = wheels; this.doors = doors; } Auto.prototype.drive = function (speed) { this.mph += speed; }; Auto.prototype.stop = function () { this.mph = 0; }; return Auto; })(); var Motorcycle = (function (_super) { __extends(Motorcycle, _super); function Motorcycle() { _super.apply(this, arguments); this.doors = 0; this.wheels = 2; } return Motorcycle; })(Auto); var bike = new Motorcycle();
Modules
Modules in TypeScript are the equivalent of namespaces in the .NET Framework. They’re a great way to organize your code and to encapsulate business rules and processes that would not be possible without this functionality (JavaScript doesn’t have a built-in way to provide this function). The module pattern, or dynamic namespacing, as in JQuery, is the most common pattern for namespaces in JavaScript. TypeScript modules simplify the syntax and produce the same effect. In the Auto example, you can wrap the code in a module and expose only the Motorcycle class, as shown in Figure 7.
The Example module encapsulates the base class, and the Motorcycle class is exposed by prefixing it with the export keyword. This allows an instance of Motorcycle to be created and all of its methods to be used, but the Auto base class is hidden.
Another nice benefit of modules is that you can merge them. If you create another module also named Example, TypeScript assumes that the code in the first module and the code in new module are both accessible through Example statements, just as in namespaces.
Modules facilitate the maintainability and organization of your code. With them, sustaining large-scale applications becomes less of a burden on development teams.
Types
The lack of type safety is one of the louder complaints I’ve heard from developers who don’t swim in the JavaScript pool every day. But type safety is available in TypeScript (that’s why it’s called TypeScript) and it goes beyond just declaring a variable as a string or a Boolean.
In JavaScript, the practice of assigning foo to x and then later in the code assigning 11 to x is perfectly acceptable, but it can drive you mad when you’re trying to figure out why you’re getting the ever-present NaN during runtime.
The type safety feature is one of the biggest advantages of TypeScript, and there are four inherent types: string, number, bool and any. Figure 8 shows the syntax for declaring the type of the variable s and the IntelliSense that the compiler provides once it knows what actions you can perform based on the type.
Figure 8 An Example of TypeScript IntelliSense
Beyond allowing the typing of a variable or function, TypeScript has the ability to infer types. You can create a function that simply returns a string. Knowing that, the compiler and tools provide type inference and automatically show the operations that can be performed on the return, as you can see in Figure 9.
Figure 9 An Example of Type Inference
The benefit here is that you see that the return is a string without having to guess. Type inference is a major help when it comes to working with other libraries that developers reference in their code, such as JQuery or even the Document Object Model (DOM).
The other way to take advantage of the type system is through annotations. Looking back, the original Auto class was declared with just wheels and doors. Now, through annotations, we can ensure that the proper types are set when creating the instance of Auto in car:
However, in the JavaScript that’s produced, the annotations are compiled away, so there’s no fat and no additional dependencies to worry about. The benefit again is strong typing and, additionally, eliminating the simple errors that are generally found during runtime.
Interfaces provide another example of the type safety offered in TypeScript. Interfaces allow you to define the shape of an object. In Figure 10, a new method named travel has been added to the Auto class and it accepts a parameter with a type of Trip.
If you try to call the travel method with anything other than the correct structure, the design-time compiler gives you an error. In comparison, if you entered this code in JavaScript, say into a .js file, most likely you wouldn’t catch an error like this until you ran the application.
In Figure 11, you can see that leveraging type annotations strongly assists not only the initial developer but also any subsequent developer who has to maintain the source.
Figure 11 Annotations Assist in Maintaining Your Code
Existing Code and Libraries
So what about your existing JavaScript code, or what if you love building on top of Node.js or use libraries such as toastr, Knockout or JQuery? TypeScript has declaration files to help. First, remember that all JavaScript is valid TypeScript. So if you have something homegrown, you can copy that code right into the designer and the compiler will produce the JavaScript one for one. The better option is to create your own declaration file.
For the major libraries and frameworks, a gentleman by the name of Boris Yankov (twitter.com/borisyankov on Twitter) has created a nice repository on GitHub (github.com/borisyankov/DefinitelyTyped) that has a number of declaration files for some of the most popular JavaScript libraries. This is exactly what the TypeScript team hoped would happen. By the way, the Node.js declaration file was created by the TypeScript team and is available as a part of the source code.
Creating a Declaration File
If you can’t locate the declaration file for your library or if you’re working with your own code, you’ll need to create a declaration file. You start by copying the JavaScript into the TypeScript side and adding the type definitions, and then use the command-line tool to generate the definition file (*.d.ts) to reference.
Figure 12 shows a simple script for calculating grade point average in JavaScript. I copied the script into the left side of the editor and added the annotations for the types, and I’ll save the file with the .ts extension.
Figure 12 Creating a Declaration File
Next, I’ll open a command prompt and use the TypeScript command-line tool to create the definition file and resulting JavaScript:
tsc c:\gradeAverage.ts –declarations
The compiler creates two files: gradeAverage.d.ts is the declaration file and gradeAverage.js is the JavaScript file. In any future TypeScript files that need the gradeAverage functionality, I simply add a reference at the top of the editor like this:
/// <reference path="gradeAverage.d.ts">
Then all the typing and tooling is highlighted when referencing this library, and that’s the case with any of the major libraries you may find at the DefinitelyTyped GitHub repository.
A great feature the compiler brings in declaration files is the ability to auto-traverse the references. What this means is if you reference a declaration file for jQueryUI, which in turn references jQuery, your current TypeScript file will get the benefit of statement completion and will see the function signatures and types just as if you had referenced jQuery directly. You can also create a single declaration file—say, “myRef.d.ts”—that contains the references to all the libraries you intend to use in your solution, and then make just a single reference in any of your TypeScript code.
Windows 8 and TypeScript
With HTML5 a first-class citizen in the development of Windows Store apps, developers have wondered whether TypeScript can be used with these types of apps. The short answer is yes, but some setup is needed in order to do so. At the time of this writing, the tooling available either through the Visual Studio Installer or other extensions hasn’t completely enabled the templates within the JavaScript Windows Store app templates in Visual Studio 2012.
There are three key declaration files available in the source code at typescript.codeplex.com—winjs.d.ts, winrt.d.ts and lib.d.ts. Referencing these files will give you access to the WinJS and WinRT JavaScript libraries that are used in this environment for accessing the camera, system resources and so forth. You may also add references to jQuery to get the IntelliSense and type safety features I’ve mentioned in this article.
Figure 13 is a quick example that shows the use of these libraries to access a user’s geolocation and populate a Location class. The code then creates an HTML image tag and adds a static map from the Bing Map API.
/// <reference path="winjs.d.ts" /> /// <reference path="winrt.d.ts" /> /// <reference path="jquery.d.ts" /> module Data { class Location { longitude: any; latitude: any; url: string; retrieved: string; } var locator = new Windows.Devices.Geolocation.Geolocator(); locator.getGeopositionAsync().then(function (pos) { var myLoc = new Location(); myLoc.latitude = pos.coordinate.latitude; myLoc.longitude = pos.coordinate.longitude; myLoc.retrieved = Date.now.toString(); myLoc.url = "" + myLoc.latitude + "," + myLoc.longitude + "15?mapSize=500,500&pp=47.620495,-122.34931;21;AA&pp=" + myLoc.latitude + "," + myLoc.longitude + ";;AB&pp=" + myLoc.latitude + "," + myLoc.longitude + ";22&key=BingMapsKey"; var img = document.createElement("img"); img.setAttribute("src", myLoc.url); img.setAttribute("style", "height:500px;width:500px;"); var p = $("p"); p.append(img); }); };
Wrapping Up
The features TypeScript adds to JavaScript development are small, but yield large benefits to .NET developers who are accustomed to similar features in the languages they use for regular Windows application development.
TypeScript is not a silver bullet, and it’s not intended to be. But for anyone who’s hesitant to jump into JavaScript, TypeScript is a great language that can ease the journey.
Shayne Boyer is a Telerik MVP, Nokia Developer Champion, MCP, INETA speaker and a solutions architect in Orlando, Fla. He has been developing Microsoft-based solutions for the past 15 years. Over the past 10 years, he has worked on large-scale Web applications, with a focus on productivity and performance. In his spare time, Boyer runs the Orlando Windows Phone and Windows 8 User Group, and blogs about the latest technology at tattoocoder.com.
Thanks to the following technical expert for reviewing this article: Christopher Bennage
MSDN Magazine Blog
More MSDN Magazine Blog entries >
Receive the MSDN Flash e-mail newsletter every other week, with news and information personalized to your interests and areas of focus.