Centric connect.engage.succeed

Using TypeScript to write your JavaScript app

Geschreven door Renzo Veldkamp - 08 juni 2016

Renzo Veldkamp
AngularJS is a great framework for writing JavaScript apps! However, it's still JavaScript you're writing, so it isn't (strictly) typed. You have several options to deal with this inevitable fact; one of them is to write your app in TypeScript. I really enjoy using TypeScript (TS) and this article shows you why it is such a great help.

What is TypeScript?

The TypeScript website states:

"TypeScript is a typed superset of JavaScript that compiles to plain JavaScript."

TypeScript is free and Open Source and is available as a NodeJS package. For Visual Studio 2013, you need to install the WebEssentials extension, but in Visual Studio 2015 you don’t need to install it: support for TypeScript is built into VS 2015 as you can see on the Microsoft site and GitHub.

As a developer, you write your code in TypeScript and the transpiler generates the JavaScript files for you (based on your TypeScript code).

The TypeScript files all have the ‘.ts’ extension; the generated files are ‘.js’ files. Here is an example of TypeScript code and its corresponding JavaScript code after transcompilation:

Example TS JS file

You can see that the transpiler creates an Immediately Invoked Function Expression (IIFE) and that the invocation result is stored in a variable Result, which is essentially our class instantiation or object.

The transpiler also adds a line of comment that you can ignore (please don’t remove: debugging your TypeScript code will not be possible anymore). It refers to another generated file containing mappings between your TypeScript code and the corresponding JavaScript code.

Why use TypeScript?

You may wonder why we would need TypeScript, since we have ECMAScript 5 and ECMAScript 6 / 2015 is also on its way. Like John Papa explains: the new ECMAScript standards don't replace TypeScript functionality. ECMAScript provides guidelines for the JavaScript runtime, whereas TypeScript helps developers write JavaScript code.

The most visible help is the enhanced IntelliSense. TypeScript uses the declarations of all your variables, functions, and classes to feed IntelliSense with information.

Example IntelliSense

These declarations are useful in themselves: they result in code that is easier to read. For example the constructor function contains all the actions that need to be performed when "instantiating the object" (JavaScript doesn't really instantiate an object, hence the quotes).

And you don't need to worry about things like IIFE: the transpiler does all that for you.

The advantages of using TypeScript are acknowledged by other people as well: the AngularJS development team switched from using AtScript to using TypeScript for their development of AngularJS. And TJ van Toll shows a nice comparison between TypeScript, CoffeeScript, and Dart in his article about the rise of TypeScript. TypeScript has recently become more popular than CoffeeScript and Dart.

Fair enough, TypeScript doesn't only offer advantages. It's basically another language added to your portfolio (next to C# or VB.Net, SQL, HTML, CSS, JavaScript, etc.). If you're familiar with C#, you'll see several similarities, like classes, interface definitions, and the use of lambda expressions. You will find more detailed information about the TypeScript language on the TypeScript website.

Type definition files

When using third party (JS) libraries or components, they may come with their own type definition files. Next to the JavaScript (or TypeScript) files, they include a ".d.ts" file, containing all the (interface) declarations, or type definitions. These files could contain definitions that are too strict.

I ran into this situation with the ng-grid component. It has a columnDefs property, which is either an array of column definitions or a string value indicating an observable property (of $scope: it was an Angular 1.2 app). In ng-grid.d.ts it is defined as:

Definitie column Defs

which is an optional array of IColumnDef implementations.

If you want to bind the set of columns to an observable property (and use a string value for the columnDefs), the transpiler will generate an error because the type definition expects an array of IColumnDef objects, not a string value.

To solve this problem, you can change the typedefs in two ways:

  • change the type of the columnDefs property to any
  • change the type to string (I chose this option)

Adapting existing type definitions is not a big deal, and if you submit a github pull request for your amendment, you contribute to the community as well!

If you want to create type definitions for a third party library (or your own app), I recommend reading this extensive blog post by Peter Grman. This post shows how you can add type definitions to a third party library that doesn’t have a set of type definitions yet.

How to use TypeScript

Essentially, you start by defining your interfaces. When using Visual Studio 2015, just add a TypeScript file and start coding. After saving the file, transcompilation is done automatically. There is no need for configuration, unless you want to deviate from the default settings.

I use the calculator app from my last blog post (a simple calculator, capable of very basic arithmetic functions) as an example. I defined two interfaces: an ICalculation and an ICalculator (and put them in a type definition file).

interface ICalculation {
    operation?: string;
    operand1: number;
    operand2?: number;
    result: number;
}

interface ICalculator {
    add: (operand1: number, operand2: number) => ICalculation;
    subtract: (operand1: number, operand2: number) => ICalculation;
    multiply: (operand1: number, operand2: number) => ICalculation;
    divide: (operand1: number, operand2: number) => ICalculation;

    lastResult: number;
}

The intention of the ICalculation interface is to enable a history of previous calculations. The ICalculator is rather self-explanatory. Note the lastResult which is intended for cascading multiple calculations.


Once you have your interfaces, you can write the objects which implement these interfaces. While coding, you will notice that IntelliSense provides you with a list of known TypeScript types, like number, and when you type new cal… a pop-up window is shown with the constructor arguments.

class Calculator implements ICalculator {
    public lastResult: number;

    constructor() {
        this.lastResult = 0;
    }

    add = function (op1: number, op2: number) {
        var calculation: ICalculation = new Calculation(op1, op2, 'add');
        this.lastResult = calculation.result;
        return calculation;
    }

    subtract = function (op1: number, op2: number) {
        var calculation: ICalculation = new Calculation(op1, op2, 'subtract');
        this.lastResult = calculation.result;
        return calculation;
    }

    multiply = function (op1: number, op2: number) {
        var calculation: ICalculation = new Calculation(op1, op2, 'multiply');
        this.lastResult = calculation.result;
        return calculation;
    }

    divide = function (op1: number, op2: number) {
        var calculation: ICalculation = new Calculation(op1, op2, 'divide');
        this.lastResult = calculation.result;
        return calculation;
    }
}

Finally

Having said all this, I can only recommend you to put some effort into learning the basics of TypeScript. Once you see the first type indication of IntelliSense for your variable 'results', you cannot get confused anymore about its contents (was it an array of strings, or an object with some properties?).

Writing JavaScript apps is nice (especially when using a great framework like AngularJS) and TypeScript makes writing JavaScript apps even more fun!

About Renzo  
Craft Expert Renzo Veldkamp is part of the .NET team within Craft, the development programme for IT professionals (powered by Centric). If you would like to follow his blog, sign up for Craft updates.

Tags:.NET

     
Schrijf een reactie
  • Captcha image
  • Verzenden