I'm pretty sure you agree with me when I say that Javascript is and hard bet for real world applications. No matter it you are very skilled with this expressive language, the danger of introducing bugs is always behind the corner, and too often these are so subtle to result difficult to discover and they become evident only when we get to the production stage. This is mostly because Javascript does ot have a strong type checking, so you can write code that does unwanted assignment from numeric to string or viceversa, but also because it isn't a real object oriented language, so to emulate these paradigms you have to rely on complex structures that make the code very hardly maintainable. These are the primary reasons that gave the start to the new Typescript language project.
Typescript, What's it?
As the names suggests, Typescript is something that wants to handle mainly the type checking. It is born for the use inside the modern browsers, where Javascript is usually the King, so this has led to some important choices. First of all the requirement that is able to run without a new interpreter installed into the browser, just because people today very harldy accept to download and install some kind of plugin.
So, the choice of the development team, headed by Anders Heilsberg, the father of C#, was to create a compiler able to take Typescript and convert to plain Javascript which is cross-browser. But the very smart choice made, during the planning phase, was to make Typescript a superset of Javascript itself. Indeed, if you take a Javascript code and cut and paste it in a Typescript file, it simply compiles without any error. The compiler is able to take this Javascript, infer types and automatically apply a strong type checking which harden the code without any additional effort from the developer. Then if you need some more complex structure, like interfaces, enumerators and classes, you can easily decorate your code with Typescript's specific constructs and opt in for a complete object oriented programming language.
Going deeply in the syntax of typescript is out of the scope of this article, but to illustrate the power of this language here is a short snippet of code:
1: class Animal {
2: constructor(public name: string) { }
3: move(meters: number) {
4: alert(this.name + " moved " + meters + "m.");
5: }
6: }
7:
8: class Snake extends Animal {
9: constructor(name: string) { super(name); }
10: move() {
11: alert("Slithering...");
12: super.move(5);
13: }
14: }
15:
16: class Horse extends Animal {
17: constructor(name: string) { super(name); }
18: move() {
19: alert("Galloping...");
20: super.move(45);
21: }
22: }
23:
24: var sam = new Snake("Sammy the Python");
25: var tom: Animal = new Horse("Tommy the Palomino");
26:
27: sam.move();
28: tom.move(34);
The snippets, taken from the Typescript's samples, shows the full set of object oriented tools that make the code much more readable and maintainable. It will be requires a number of articles to illustrate all the wonderful features of this language but, as I already said, it is not matter for this article. At the moment there is a question we need to answer before: What does it has to do with Windows Store apps?
Typescript for the Windows Store apps?
Since I've said that the Typescript compiler is able to generate plain Javascript, supported by all the existing browsers, is it reasonable to thinks that it should work for every platform where Javascript is the elective language.
And this is really true! In facts, one of the most popular examples of Typescript is made to run on node.js that is a server side engine based on Javascript. Windows Store apps can be written in Javascript so is it possible to use Typescript in this scenario. Unfortunately it is only partly true.
I try to explain. Typescript bases its support to third party libraries, on a special "declaration" file. This file is made to make available the types defined by an external library to the Typescript compiler. Thanks to these "d" files, the developer can reference the types of the external library and is supported by Visual Studio, with intellisense, and by the compiler to check the correspondence between a call to a method and its signature.
In the source code of the Typescript project, you can download two important files: winjs.d.ts and winrt.d.ts. Into these files are reported a number of declarations to support the usage of both WinJS library and the Windows.* namespace inside the WinRT core. So, the first step to work with TypeScript in a Windows Store app, just after the project is created is to download these files an add them to the project. I usually use a "Script > typings > library" folder structure, so when I add othe typings from nuget they fall automatically inside the same folder. Watch at the figure on the side.
In the same folder, you see also a jquery.d.ts file that is in place to show how you can add other libraries. For this purpose I suggest to open the nuget references and search for "DefinitelyTyped". All the packages outpu of this search are an impostant base for every Typescript developments, since they contains definition for a huge number of Javascript libraries, most of them also useful in a WinJS environment.
The references to these files have to be added to each typescript source where they need to be used. To simplify this operation, and to have a place to put additional definitions manually (I'll speak about this in a few), you can add a "win.d.ts" file as follow:
1: /// <reference path="typings/winrt/winrt.d.ts" />
2: /// <reference path="typings/winjs/winjs.d.ts" />
3:
4: declare var msSetImmediate: (expression: any) => void;
Thanks to this file you can simply add a reference to it and all the referenced libraries are automatically available, together with the other additional declarations like the msSetImmediate in the snippet.
Once these files are in place, you can start creating new "ts" files, but you have to be aware that they are compiled only when you save the file. To solve this problem and support the build together with the other project files you have to unload the project, open the project file (use the "unload project" action in context menu, then again in search for Edit *.jsproj" after the project has been unloaded) and add the following sections:
1: <ItemGroup>
2: <AvailableItemName Include="TypeScriptCompile" />
3: </ItemGroup>
4: <ItemGroup>
5: <TypeScriptCompile Include="$(ProjectDir)\**\*.ts" Exclude="$(ProjectDir)\**\*.d.ts" />
6: </ItemGroup>
7: <Target Name="BeforeBuild">
8: <Exec Command=""$(PROGRAMFILES)\Microsoft SDKs\TypeScript\tsc" -target ES5 @(TypeScriptCompile ->'"%(fullpath)"', ' ')" />
9: </Target>
10: <Target Name="AfterBuild">
11: </Target>
Thanks to these sections, you have to add to the very low end of the project file, the compilation task also includes all the *.ts files and excludes the "*.d.ts" that do not have to be compiled together with source code. After reloading the project I suggest to manually delete the "bin" folder because it may contains some spurious files generated by previous compilations. These may generate unwanted errors. Now the project is ready and you can try to compile and see the "Build succeded" message on the bottom left corner.
Let's start coding...
Ok, once the project is ready, it is time to chill your enthusiasm. The very first thing you may try is to convert a page file, like the default.js" to a full typescript file. I've also tried this same action and I had to note that this is not always possible. The problem here is that the winjs and winrt definition files, we have added to the project, are far to be complete and these cause a number of compilation errors. To work around to these errors you have to manually add the declarations to the winjs and winrt definition files. Unfortunately, future updates of these files, may override your changes. So, the better is to take advantage of a Typescript compiler feature that is able to merge the content of namespaces and types defined in different files. You can add your additional definitions in the "win.d.ts" and have them merged with definitions in the regular files.
1: /// <reference path="typings/winrt/winrt.d.ts" />
2: /// <reference path="typings/winjs/winjs.d.ts" />
3:
4: declare var msSetImmediate: (expression: any) => void;
5:
6: module WinJS
7: {
8: export module Binding
9: {
10: export var optimizeBindingReferences: bool;
11: }
12: }
13:
14: interface ActivatedEventListener
15: extends Event
16: {
17: detail: any;
18: setPromise(promise: any): void;
19: }
In this snippet I've added the definitions for the missing points that appear in the default.js of the blank project. Thanks to these definitions you can now convert the file to a full typescript source as follow:
1: /// <reference path="../Scripts/win.d.ts" />
2: /// <reference path="../Scripts/utils.ts" />
3:
4: (function ()
5: {
6: "use strict";
7:
8: WinJS.Binding.optimizeBindingReferences = true;
9:
10: var app = WinJS.Application;
11: var activation = Windows.ApplicationModel.Activation;
12:
13: app.onactivated = (args: ActivatedEventListener) =>
14: {
15: if (args.detail.kind === activation.ActivationKind.launch)
16: {
17: if (args.detail.previousExecutionState !== activation.ApplicationExecutionState.terminated)
18: { }
19: else
20: { }
21:
22: args.setPromise(WinJS.UI.processAll());
23: }
24: };
25:
26: app.oncheckpoint = (args) =>
27: {
28:
29: };
30:
31: app.start();
32: })();
I suspect that handling this kind of errors is not matter for rookie Typescript developers. They may become fastly something that is hard to understand and time consuming. So you have to be very careful with this approach and take in serious consideration another alternative. Use typescript only for the object model and the business logic and continue to use Javascript for the page files.
Writing logic with Typescript
It may seems silly, but your business logic and domain model are the places where Typescript may give the best contribute. Infact you can easily model your domain with a real object oriented and type checked language, and finally convert them to javascript to reference in pages. In the following snippet I've added a DataService class and a number of classes used to describe and deserialize the response of the USGS Earthquake service:
1: /// <reference path="win.d.ts" />
2:
3: module utils
4: {
5: export class DataService
6: {
7: static USGSUri: string = 'http://earthquake.usgs.gov/earthquakes/feed/geojson/2.5/week';
8:
9: static getPastWeekEvents(): WinJS.Promise
10: {
11: return WinJS.xhr(
12: {
13: url: DataService.USGSUri,
14: data: null, headers: null, type: null, user: null, password: null, responseType: null
15: }).then(
16: (request) =>
17: {
18: var data: EarthquakeResponse = <EarthquakeResponse>JSON.parse(request.response);
19: var result: Earthquake[] = [];
20:
21: for (var i = 0; i < data.features.length; i++)
22: {
23: var item = data.features[i];
24:
25: if (data.features[i].properties.mag < 0)
26: data.features[i].properties.mag = -data.features[i].properties.mag;
27:
28: var eq: Earthquake = new Earthquake();
29: eq.id = item.id;
30: eq.magnitude = Math.round(item.properties.mag);
31: eq.description = item.properties.place;
32: // other properties...
33:
34: result.push(eq);
35: }
36:
37: return result;
38: });
39: }
40: }
41:
42: export class EarthquakeResponse
43: {
44: features: EarthquakeItem[];
45: }
46:
47: export class EarthquakeItem
48: {
49: id: string;
50: type: string;
51: properties: EarthquakeDetail;
52: geometry: EarthquakeGeometry;
53: }
54:
55: export class EarthquakeDetail
56: {
57: mag: number;
58: place: string;
59: time: number;
60: updated: number;
61: tz: number;
62: url: string;
63: felt: string;
64: cdi: string;
65: mmi: string;
66: alert: string;
67: status: string;
68: tsunami: string;
69: sig: string;
70: net: string;
71: code: string;
72: ids: string;
73: sources: string;
74: types: string;
75: }
76:
77: export class EarthquakeGeometry
78: {
79: type: string;
80: coordinates: number[];
81: }
82:
83: export class Earthquake
84: {
85: id: string;
86: description: string;
87: uri: string;
88: uTCDate: Date;
89: localDate: Date;
90: epicenterDate: Date;
91: magnitude: number;
92: elevation: number;
93: latitude: number;
94: longitude: number;
95: level: number;
96: }
97: }
98:
The code I've provided here exposes a method to download a feed, deserializes it and then extract the information in a known type that is useful for binding to the user interface. It supports the WinJS promises as explained in the previous article. It is a good example of how Typescript can make stronger your code. Here is how to use the compiled output, you have to ensure is part of the project adding the "utils.js" to the solution:
1: utils.DataService.getPastWeekEvents()
2: .then((eqs) =>
3: {
4: // bind earthquakes to the interface here...
5: });
Once you get in touch with Typescript I'm pretty sure you will be very enthusiast of the support it does to you while coding. I hope that Microsoft adds as soon as possible a regular and complete suppor to this smart language, but for the moment you can start to take advantage of it right now.