Saturday, Dec 29, 2012, 4:11 PM
TypeScript Templates for Windows 8
As soon as I saw Anders’ talk on TypeScript, I fell in love. If you’re not familiar with it, TypeScript adds a lot of necessary features to JavaScript to make it suitable for building real apps, while still “compiling down” to JavaScript to maintain JS’s single biggest advantage: ubiquity. Further, TypeScript has tooling inside Visual Studio so that it works nicely with a wide variety of Windows projects, including Win8/JS projects.
However, while Microsoft has made a nice Win8/TS sample available, there are currently no Visual Studio project templates for building my own apps. Luckily, it was easy enough to build some:
You’ll notice three new templates: TypeScript versions of Blank App, Fixed Layout App and Navigation App. All three projects generate code that acts the same, except the code is in TypeScript instead of JavaScript (although the JavaScript is generated and very visible to your inspection).
I didn’t build the Grid App or Split App templates yet, since there is a lot of code there. I also haven’t ported any of the item templates. Now that I have the Navigation App template done (which includes an empty Page Control), the Grid and Split and other item templates will all flow from there (eventually : ).
JavaScript Patterns to TypeScript Constructs
Moving JavaScript to TypeScript involves two major pieces: porting the code from JS patterns to TS language constructs and bringing in references to the types that are used.
The first step, moving from JS patterns to TS language constructs, largely involved modules, classes and functions. For example, the navigator.js file defines the PageControlNavigator class in the Application namespace using JS patterns:
// navigator.js (function () { "use strict"; var appView = Windows.UI.ViewManagement.ApplicationView; var nav = WinJS.Navigation; WinJS.Namespace.define("Application", { PageControlNavigator: WinJS.Class.define( // Define the constructor function for the PageControlNavigator. function PageControlNavigator(element, options) { ...Application.navigator = this;}, { home: "", /// <field domElement="true" /> _element: null, _lastNavigationPromise: WinJS.Promise.as(), _lastViewstate: 0, // This is the currently loaded Page object. pageControl: { get: function () { ... } }, ... } ) }); })();
The common pattern for a module that contains private and public parts is to use a self-executing anonymous function (which wraps all the code in navigator.js) to make everything private and then to use helpers to expose public parts explicitly (like the use of WinJS.Namespace.define). Further, to define a class is a matter of gathering up a constructor function with a set of member properties and functions, which is what the WinJS.Class.define helper does. Finally, right in the middle of that is the exposing of a namespace-wide property called Application.navigator, which makes it available to anyone using the Application namespace.
TypeScript provides actual language constructs for these patterns:
///<reference path='../declare/declare.ts' /> // navigator.ts module Application { "use strict"; var appView = Windows.UI.ViewManagement.ApplicationView; var nav = WinJS.Navigation; export var navigator: PageControlNavigator; interface PageControl { getAnimationElements: () => Element; updateLayout: ( element: Element, viewState: Windows.UI.ViewManagement.ApplicationViewState, lastViewstate: Windows.UI.ViewManagement.ApplicationViewState) => void; } export class PageControlNavigator { home: string = ""; _element: Element = null; _lastNavigationPromise: WinJS.Promise = WinJS.Promise.as(); _lastViewstate: Windows.UI.ViewManagement.ApplicationViewState; constructor (element, options) { ... } // This is the currently loaded Page object. get pageControl(): PageControl { ... } // ... } }
In this TypeScript code, you’ll see the module, export and class keywords that define the elements we were defining via patterns before. Further, the use of the interface keyword lets you define a contract for an argument or variable that the TypeScript compiler can check for you as it generates the corresponding JavaScript. Finally, notice the use of the type annotations after a colon, e.g. the PageControlNavigator type on the exported navigator variable, to give the TypeScript compiler more information. All of these constructs help you to be explicit about what you’re defining, which helps you track down errors and gives you better Intellisense as you code.
As I mentioned, TypeScript provides syntax on top of JavaScript, the idea being that all JavaScript code is already TypeScript code. Further, the TypeScript compiles produces JavaScript as it’s output. When you’re editing a TypeScript file in Visual Studio, you can see the corresponding JavaScript, which helps experienced JavaScript programmers bootstrap their way to TypeScript.
You’ll notice in this screenshot that TypeScript introduces shortcut syntax for function objects. For example, the following code from home.js:
WinJS.UI.Pages.define("/pages/home/home.html", { ready: function (element, options) { // TODO: Initialize the page here. } });
can be written in TypeScript as follows:
WinJS.UI.Pages.define("/pages/home/home.html", { ready: (element, options) => { // TODO: Initialize the page here. }, });
The use of the TypeScript lambda syntax is both shorter and works better when it comes to your intuition of what the “this” keyword means.
The other thing to notice about most .ts files is one or more reference lines at the top that look like this:
///<reference path='../../declare/declare.ts' />
This is the TS way to do “include” or “import” of code from other TS files, instead of relying on an HTML container to pull in the right files.
TypeScript Declarations
A lot of the work porting the Win8/JS templates to TypeScript was replacing the use of JS patterns with TS constructs (which, ironically, generated back the same JS code I started with), but an equal amount of the work was in building TypeScript declaration files (*.d.ts files). The idea of a declaration file is that the JavaScript community has created a large number of libraries, none of which have associated TypeScript type information. TypeScript allows you to augment the type information for existing JavaScript libraries, e.g. jQuery, Knockout, WinJS, etc., with external files called declaration files.
The WinJS sample I mentioned earlier (Encyclopedia) provides a number of declaration files that provide type information for the HTML DOM, the WinRT object model, jQuery and for WinJS. Unfortunately, the one for WinJS is far from complete, which meant that a lot of the work I did to get the Win8/TS templates compiling without warnings was augmenting that file. All of the declarations files needed to make the templates compiled as generated are provided in the “declare” folder, but I’m sure there are holes that you’re going to run into as you add your own code. Of course, the authoritative winjs.d.ts file is part of the TypeScript distribution, so I’ll work with the nice folks on the TypeScript codeplex project to get my changes merged in.
Installing the Win8/TS Templates
To get started using the Win8/TS templates I’ve built, you’ll first need to install the TypeScript plug-in for Visual Studio 2012. Currently these templates have been tested under TS 0.8.1.1 only and the generated .jsproj files have this path hard-coded in. The web-based HTML Application with TypeScript template uses some trick to do away with hard-coded paths that I have yet to figure out.
You can download the Win8/TS samples from here, extract the three folders (blankts, blankfixedts and navts) into your VS2012 JavaScript project template folder, e.g. C:\Program Files (x86)\Microsoft Visual Studio 11.0\Common7\IDE\ProjectTemplates\JavaScript\Windows Store\1033. Once the files are there, shutdown all instances of Visual Studio 2012 and execute “devenv.exe /InstallVSTemplates” as admin. If you have multiple copies of Visual Studio installed, make sure you’re executing the one for VS2012.
Once you’ve completed those steps successfully, you should see three new TypeScript-based templates as shown in the very first figure of this blog post. Enjoy.