Creating a simple TypeScript web app

Friday, December 4, 2015

TypeScript: usable JavaScript

I'm not a big fan of "vanilla" JavaScript. Compared to C# and VB.NET it's convoluted, quirky, and, of course, typeless. As a result, I try to keep the amount of JavaScript I need to a minimum at all costs.

TypeScript, however, is a different story. For me, it makes JavaScript logical, understandable, and therefore usable. It introduces classes and typing in a way that feels completely natural to anyone familiar with C#. And in some respects it's ahead of C# (which should come as no surprise since TypeScript and C# were conceived by the same man).

Setting up a TypeScript web site in Visual Studio 2015 Update 1 is simple, but there are a few things to remember. This is what I learned.

Creating the web app

This is the really simple part: create a new project in Visual Studio 2015. Under Templates select the TypeScript node and you'll see HTML Application with TypeScript on the right. Set up directories etc. and click OK.

What you'll get is an absolutely minimal site:

  • index.html - your home page
  • app.ts - the main TypeScrript file
  • app.css - a really basic style sheet
  • web.config (plus web.Debug.config and web.Release.config) - an also absolutely minimal web configuration file

Note that no folders are created: no css, no scripts, and certainly no ControllersViews, etc.

index.html is the simplest possible HTML5 document. It references app.css, of course, and app.js. Not app.ts, because that's the TypeScript source file that is automatically compiled into its JavaScript equivalent - in app.js.

app.ts contains a single class named Greeter and a startup method that's called on window.onload(). That's it, basically.

Adding classes

At first, I set off creating modules. I wanted a test class (Person, naturally) and since I heard about modules as the way to go in modern JavaScript, I typed:

export module Persons {
  class Person {
  }
}

into a file persons.ts and tried to include that in app.ts by typing

import persons = require("./persons.ts");

But this only resulted in an error: Cannot compile modules unless the --module flag is provided. Some searching led me to the project properties where I had to choose a module system: AMD, CommonJS, UMD or System. But blindly choosing one didn't help, and choosing another didn't work either.

Way to go. Time to do some reading.

It turns out that you shouldn't really bother with modules in TypeScript unless you have to - and often, you don't. Following a few simple rules will do the trick:

  1. Don't import or export your own source files. All TypeScript source files can use classes in other source files in the same project by default, so there is no need to export anything, nor to import anything. You don't even need reference comments like
    /// <reference path="..."/>
    Therefore, how you distribute your classes over source files is entirely up to you.
  2. For your web pages to work, however, the browser does need to know about all the classes in your project. So you need script tags for all your JavaScript sources in your HTML. The order of these doesn't matter, but it is a good idea to move all script tags to just before the body's closing tag - in the generated code the script tags are in the head element. (There is a way to avoid referencing each individual JavaScript file - read on!)

Compile on save and debugging

All TypeScript sources are compiled when saved, so testing is easy. Just start your site and press F5 in the browser when anything changes, or press Ctrl+Alt+Enter to refresh all connected browsers using BrowserLink.

Adding jQuery

To use jQuery in your web app, you have to include it in the HTML, of course. But once you have done that, you still can't access jQuery's $ from your TypeScript sources. To get Typescript to recognize $ and to provide Intellisense for it, you have to include the typings for jQuery. In practice, this amounts to going to DefinitelyTyped.org, downloading jquery.d.ts and including that in your project. From then on, you get full Visual Studio assistance for jQuery in your TypeScript files.

Adding Razor views

While I don't often need full-blown MVC sites, I really love the Razor view system with its layout pages and simple, clean syntax. Fortunately, adding Razor to your project is simple: just add an item to your project from the Razor templates - a Web Page or a Layout Page or a Helper. When you add one of these for the first time, Visual Studio will add a few references to your project, including System.Web.WebPagesSystem.Web.Infrastructure and System.Web.Razor.

From then on, you can code your heart out using Razor views in your favorite language. Just rename your index.html page to index.cshtml or index.vbhtml to get started.

Combining TypeScript output into a single JavaScript file

There is a way to eliminate the need to add a script tag for each JavaScript output file. Open the project's properties page, select the TypeScript Build tab and check Combine JavaScript output into file:. Supply a file name, and include only that file in your HTML. I had some issues with this in the past, but it seems to work OK now.

Re-introducing folders

And while I'm sometimes a little overwhelmed by the multitude of folders in MVC web applications, a little structure is in order, I believe. So I created a css folder (and moved app.css into it), a scripts folder for all TypeScript files, and a Views folder with my _Layout.cshtml. Your main view (index.cshtml) cannot be moved into a folder, because otherwise there would be no default page to serve. Also, the combined JavaScript output should go to scripts\all.js instead of all.js.

This requires a few changes:

  • The reference to the stylesheet app.css in _Layout.cshtml needs to change to href="/css/app.css"
  • The reference to the all.js in _Layout.cshtml needs to change to src="/scripts/all.js"
  • The reference to _Layout.cshtml in index.cshtml needs to change to Layout = "~/Views/_Layout.cshtml"

So it's really a lot simpler than I thought. Visual Studio and TypeScript take care of all the hard work. You can concentrate on your Razor-markup (plus some css, of course), and put all your TypeScript wherever you like, as long as you include the corresponding .js-files in your Layout.cshtml. And not even that is necessary if you combine the TypeScript output into a single .js file.