Chapter 1. TypeScript – Tools and Framework Options

JavaScript is a truly ubiquitous language. Just about every website that you visit in the modern world will have some sort of JavaScript component embedded in it, in order to make the site more responsive, more readable, or more attractive to use. Think about the most impressive website that you have visited over the past few months. Was it visually appealing? Did it have some sort of clever presentation? Did it engage you as a user, by giving you a completely new way of discovering car-insurance, or image-sharing, or news articles?

This is the power of JavaScript. JavaScript is the icing on the cake of the internet experience, that makes millions of people around the world go "wow. That's cool". And it generates revenue. Two websites may offer the same product, at the same price, but the one that engages the client – and makes them enjoy the web experience – is the site that will attract the most followers and be the most successful. If this website can also be seamlessly reproduced on desktops, mobiles or tablets, then the target audience – and the target revenue – can be increased exponentially.

On the flip-side, though, JavaScript is also responsible for the annoying side of the Internet. Those annoying advertisements, where you have to wait for 5 seconds before clicking on the skip button. Or websites that do not quite work on older browsers, or don't render correctly on tablets and mobile phones. It can be argued that many websites would be better off without JavaScript.

An engaging web experience can also make the difference in corporate web applications. A clunky, difficult to use, and slow web application will turn otherwise keen corporate users completely against your application. Remember that your typical corporate user is comparing their work experience to their daily web experience – of well designed, responsive, intuitive interfaces. After all, they are generally users of the most popular websites out there, and come to expect the same responsiveness at work.

Most of this enhanced user experience comes from the effective use of JavaScript. Asynchronous JavaScript requests allow your web page to render content to the user faster – while waiting for backend processes to do the heavy, time consuming data crunching tasks.

The JavaScript language is not a difficult language to learn, but it does present challenges when writing large, complex programs. Being an interpreted language, JavaScript has no compilation step, and so is executed on the fly. For programmers that are used to writing code in a more formal environment – using compilers, strong typing and well established programming patterns – JavaScript can be a completely foreign environment.

TypeScript bridges this gap. It is a strongly typed, object-oriented, compiled language that allows you as a programmer, to re-use the concepts and ideas of well-established object-oriented languages – in JavaScript. The TypeScript compiler generates JavaScript that adheres to these strongly typed, object-oriented principles – but at the same time is just pure JavaScript. As such, it will run successfully wherever JavaScript can run – in the browser, on the server, or on modern mobile devices.

This chapter is divided into two main sections. The first section is a quick overview of some of the benefits that TypeScript brings to the JavaScript development experience. The second section of this chapter deals with setting up a TypeScript development environment.

If you are an experienced TypeScript programmer, and you already have a development environment set up, then you might want to skip this chapter. If you have never worked with TypeScript before, and have picked up this book because you want to understand what TypeScript can do for you, then read on.

We will cover the following topics in this chapter:

  • The benefits of TypeScript
    • Compilation
    • Strong Typing
    • Integration with popular JavaScript libraries
    • Encapsulation
    • Private and public member variables
  • Setting up a development environment
    • Visual Studio
    • WebStorm
    • Brackets and Grunt

What is TypeScript?

TypeScript is both a language and a set of tools to generate JavaScript. It was designed by Anders Hejlsberg at Microsoft (the designer of C#), as an open-source project, to help developers write enterprise scale JavaScript. JavaScript has become widely adopted by programmers around the world – as it can run in any browser on any operating system. With the creation of Node, JavaScript can now also run on the server, desktop or mobile.

TypeScript generates JavaScript – it's as simple as that. Instead of requiring a completely new runtime environment, TypeScript generated JavaScript can re-use all of the existing JavaScript tools, frameworks, and wealth of libraries that are available for JavaScript. The TypeScript language and compiler, however, brings the development of JavaScript closer to a more traditional object-oriented experience.

EcmaScript

JavaScript as a language has been around for a long time, and is also governed by a language feature standard. The language defined in this standard is called ECMAScript, and each browser must deliver functions and features that conform to this standard. The definition of this standard helped the growth of JavaScript and the web in general, and allowed websites to render correctly on many different browsers on many different operating systems. The ECMAScript standard was published in 1999 and is known as ECMA-262, third edition.

With the popularity of the language, and the explosive growth of internet applications, the ECMAScript standard needed to be revised and updated. This process resulted in a draft specification for ECMAScript, called the fourth edition. Unfortunately, this draft suggested a complete overhaul of the language, and was not well received. Eventually, leaders from Yahoo, Google and Microsoft tabled an alternate proposal which they called ECMAScript 3.1. This proposal was numbered 3.1, as it was a smaller feature set of the third edition, and sat "between" edition 3 and 4 of the standard.

This proposal was eventually adopted as the fifth edition of the standard, and was called ECMAScript 5. The ECMAScript fourth edition was never published, but it was decided to merge the best features of both the fourth edition and the 3.1 feature set – into a sixth edition named ECMAScript Harmony.

The TypeScript compiler has a parameter that can be modified to target different versions of the ECMAScript standard. TypeScript currently supports ECMAScript 3, ECMAScript 5 and ECMAScript 6. When the compiler runs over your TypeScript, it will generate compile errors if the code you are attempting to compile is not valid for that particular standard. The team at Microsoft has also committed to follow the ECMAScript standards in any new versions of the TypeScript compiler, so as and when new editions are adopted, the TypeScript language and compiler will follow suit.

An understanding of the finer details of what is included in each release of the ECMAScript standard is outside of the scope of this book, but it is important to know that there are differences. Some browser versions do not support ES5 (IE8 is an example), but most do. When selecting a version of ECMAScript to target for your projects, you will need to consider which browser versions you will be supporting.

The benefits of TypeScript

To give you a flavor of the benefits of TypeScript (and this is by no means the full list), let's take a very quick look at some of the things that TypeScript brings to the table:

  • A compilation step
  • Strong or static typing
  • Type definitions for popular JavaScript libraries
  • Encapsulation
  • Private and public member variable decorators

Compiling

One of the most frustrating things about JavaScript development is the lack of a compilation step. JavaScript is an interpreted language, and therefore needs to be run in order to test that it is valid. Every JavaScript developer will tell horror stories of hours spent trying to find bugs in their code, only to find that they have missed a stray closing brace { , or a simple comma , - or even a double quote " where there should have been a single quote '. Even worse, the real headaches arrive when you misspell a property name, or unwittingly re-assign a global variable.

TypeScript will compile your code, and generate compilation errors where it finds these sort of syntax errors. This is obviously very useful, and can help to highlight errors before the JavaScript is run. In large projects, programmers will often need to do large code merges – and with today's tools doing automatic merges – it is surprising how often the compiler will pick up these types of errors.

While tools to do this sort of syntax checking – like JSLint – have been around for years, it is obviously beneficial to have these tools integrated into your IDE. Using TypeScript in a continuous integration environment will also fail a build completely when compilation errors are found – further protecting your programmers against these types of bugs.

Strong Typing

JavaScript is not strongly typed. It is a language that is very dynamic, as it allows objects to change their properties and behavior on the fly. As an example of this, consider the following code:

var test = "this is a string";
test = 1;
test = function(a, b) {
    return a + b;
}

On the first line of this code snippet, the variable test is bound to a string. It is then assigned a number, and finally is redefined to be a function that expects two parameters. Traditional object oriented languages, however, will not allow the type of a variable to change – hence they are called strongly typed languages.

While all of the preceding code is valid JavaScript - and could be justified - it is quite easy to see how this could cause runtime errors during execution. Imagine that you were responsible for writing a library function to add two numbers, and then another developer inadvertently re-assigned your function to instead subtract these numbers.

These types of errors may be easy to spot in a few lines of code, but it becomes increasingly difficult to find and fix these as your code base, and your development team grows.

Another feature of strong typing is that the IDE you are working in can understand what type of variable you are working with, and can bring better autocomplete or Intellisense options to the fore.

TypeScript's "syntactic sugar"

TypeScript introduces a very simple syntax to check the type of an object at compile time. This syntax has been referred to as "syntactic sugar", or more formally, type annotations. Consider the following TypeScript code:

var test: string = "this is a string";
test = 1;
test = function(a, b) { return a + b; }

Note on the first line of this code snippet, we have introduced a colon : and a string keyword between our variable and it's assignment. This type annotation syntax means that we are setting the type of our variable to be of type string, and that any code that does not use it as a string will generate a compile error. Running the preceding code through the TypeScript compiler will generate two errors:

error TS2011: Build: Cannot convert 'number' to 'string'.
error TS2011: Build: Cannot convert '(a: any, b: any) => any' to 'string'.

The first error is fairly obvious. We have specified that the variable test is a string, and therefore attempting to assign a number to it will generate a compile error. The second error is similar to the first, and is in essence saying that we cannot assign a function to a string.

In this way, the TypeScript compiler introduces strong, or static typing to your JavaScript code, giving you all of the benefits of a strongly typed language. TypeScript is therefore described as a "superset" of JavaScript. We will explore typing in more detail in the next chapter.

Type definitions for popular JavaScript libraries

As we have seen, TypeScript has the ability to "annotate" JavaScript, and bring strong typing to the JavaScript development experience. But how do we strongly type existing JavaScript libraries? The answer is surprisingly simple: by creating a definition file. TypeScript uses files with a .d.ts extension as a sort of "header" file, similar to languages such as C++, to superimpose strongly typing on existing JavaScript libraries. These definition files hold information that describes each available function and variable of the library, along with their associated type annotations.

Let's take a quick look at what a definition would look like. As an example, consider a function from the popular Jasmine unit testing framework called describe:

var describe = function(description, specDefinitions) {
  return jasmine.getEnv().describe(description, specDefinitions);
};

This function has two parameters, description and specDefinitions. Just reading this JavaScript, however, does not tell us what sort of parameters these are meant to be. Is the specDefinitions argument a string, or an array of strings, a function or something else? In order to figure this out, we would need to have a look through the Jasmine documentation found at http://jasmine.github.io/2.0/introduction.html. This documentation provides us with a helpful sample of how to use this function:

describe("A suite", function () {
    it("contains spec with an expectation", function () {
        expect(true).toBe(true);
    });
});

From the documentation, then, we can easily see that the first parameter is a string, and the second parameter is a function. There is nothing in the JavaScript language, however, that forces us to conform to this API. As mentioned before, we could easily call this function with two numbers – or inadvertently switch the parameters around, sending a function first, and a string second. We will obviously start getting runtime errors if we do this, but TypeScript – using a definition file – can generate compile time errors before we even attempt to run this code.

Let's take a look at a piece of the jasmine.d.ts definition file:

declare function describe(
    description: string, specDefinitions: () => void
): void;

This is the TypeScript definition for the describe function. Firstly, declare function describe tells us that we can use a function called describe, but that the implementation of this function will be provided at runtime.

Clearly, the description parameter is strongly typed to be of type string, and the specDefinitions parameter is strongly typed to be a function that returns void. TypeScript uses the double braces () syntax to declare functions, and the fat arrow syntax to show the return type of the function. So () => void is a function that does not return anything. Finally, the describe function itself will return void.

If our code were to try and pass in a function as the first parameter, and a string as the second parameter (clearly breaking the definition of this function) as shown in the following example:

describe(() => { /* function body */}, "description");

The TypeScript compiler will immediately generate the following errors:

error TS2082: Build: Supplied parameters do not match any signature of call target: Could not apply type "string" to argument 1 which is of type () => void

This error is telling us that we are attempting to call the describe function with invalid parameters. We will look at definition files in more detail in later chapters, but this example clearly shows that TypeScript will generate errors if we attempt to use external JavaScript libraries incorrectly.

Definitely Typed

Soon after TypeScript was released, Boris Yankov started a GitHub repository to house definition files, at DefinitelyTyped (https://github.com/borisyankov/DefinitelyTyped). This repository has now become the first port of call for integrating external libraries into TypeScript, and currently holds definitions for over 500 JavaScript Libraries.

Encapsulation

One of the fundamental principles of object-oriented programming is encapsulation: The ability to define data, as well as a set of functions that can operate on that data, into a single component. Most programming languages have the concept of a class for this purpose – providing a way to define a template for data and related functions.

Let's first take a look at a simple TypeScript class definition:

class MyClass {
    add(x, y) {
        return x + y;
    }
}

var classInstance = new MyClass();
console.log(classInstance.add(1, 2));

This code is pretty simple to read and understand. We have created a class, named MyClass, with a single function named add. To use this class, we simply create an instance of it, and call the add function with two arguments.

JavaScript, unfortunately, does not have a class keyword, but instead uses functions to reproduce the functionality of classes. Encapsulation through classes is accomplished by either using the prototype pattern, or by using the closure pattern. Understanding prototypes and the closure pattern, and using them correctly, is considered a fundamental skill when writing enterprise-scale JavaScript.

A closure is essentially a function that refers to independent variables. This means that variables defined within a closure function 'remember' the environment in which they were created. This provides JavaScript with a way to define local variables, and provide encapsulation. Writing the MyClass definition in the preceding code, using a closure in JavaScript would look something like this:

var MyClass = (function () {
    // the self-invoking function is the 
    // environment that will be remembered
    // by the closure
    function MyClass() {
        // MyClass is the inner function,
        // the closure
    MyClass.prototype.add = function (x, y) {
        return x + y;
    };
    return MyClass;
})();
var classInstance = new MyClass();
console.log("result : " + classInstance.add(1, 2));

We start with a variable called MyClass, and assign it to a function that is executed immediately – note the })(); syntax near the bottom of the code snippet. This syntax is a common way to write JavaScript in order to avoid leaking variables into the global namespace. We then define a new function named MyClass, and return this new function to the outer calling function. We then use the prototype keyword to inject a new function into the MyClass definition. This function is named add and takes two parameters, returning their sum.

The last two lines of the code show how to use this closure in JavaScript. Create an instance of the closure type, and then execute the add function. Running this in the browser will log result: 3 to the console, as expected.

Tip

Downloading the example code

You can download the example code files from your account at http://www.packtpub.com for all the Packt Publishing books you have purchased. If you purchased this book elsewhere, you can visit http://www.packtpub.com/support and register to have the files e-mailed directly to you.

Looking at the JavaScript code versus the TypeScript code, we can easily see how simple TypeScript looks, compared to the equivalent JavaScript. Remember how we mentioned that JavaScript programmers can easily misplace a brace {, or a bracket (? Take a look at the last line in the closure definition: })(); Getting one of these brackets or braces wrong can take hours of debugging to find.

TypeScript classes generate closures

The JavaScript closure as shown in the preceding code snippet, is actually the output of the TypeScript class definition. So TypeScript actually generates closures for you.

Note

Adding the concept of classes to the JavaScript language has been talked about for years, and is currently a part of the ECMAScript sixth Edition (Harmony) standard – but this is still a work in progress. Microsoft has committed to follow the ECMAScript standard in the TypeScript compiler, as and when these standards are published.

Public and private accessors

A further object-oriented principle that is used in encapsulation is the concept of data hiding – the ability to have public and private variables. Private variables are meant to be hidden to the user of a particular class – as these variables should only be used by the class itself. Inadvertently exposing these variables outside of a class can easily cause runtime errors.

Unfortunately, JavaScript does not have a native way of declaring variables private. While this functionality can be emulated using closures, a lot of JavaScript programmers simply use the underscore character _ to denote a private variable. At runtime though, if you know the name of a private variable – you can easily assign a value to it. Consider the following JavaScript code:

var MyClass = (function() {
    function MyClass() {
        this._count = 0;
    }
    MyClass.prototype.countUp = function() {
        this._count ++;
    }
    MyClass.prototype.getCountUp = function() {
        return this._count;
    }
    return MyClass;
}());

var test = new MyClass();
test._count = 17;
console.log("countUp : " + test.getCountUp());

The MyClass variable is actually a closure – with a constructor function, a countUp function and a getCountUp function. The variable _count is supposed to be a private member variable, one that is used only within the scope of the closure. Using the underscore naming convention gives the user of this class some indication that the variable is private, but JavaScript will still allow you to manipulate the variable _count. Take a look at the second last line of the code snippet. We are explicitly setting the value of the supposed private variable _count to 17 – which is allowed by JavaScript, but not desired by the original creator of the class. The output of this code would be countUp: 17.

TypeScript, however, introduces the public and private keywords that can be used on class member variables. Trying to access a class member variable that has been marked as private will generate a compile time error. As an example of this, the JavaScript code above can be written in TypeScript as follows:

class MyClass {
    private _count: number;
    constructor() {
        this._count = 0;
    }
    countUp() {
        this._count++;
    }
    getCount() {
        return this._count;
    }
}

var classInstance = new MyClass();
console.log(classInstance._count);

On the second line of our code snippet, we have declared a private member variable named _count. Again, we have a constructor, a countUp and a getCount function. If we compile this TypeScript code, the compiler will generate an error:

error TS2107: Build: 'MyClass._count' is inaccessible.

This error is generated because we are trying to access the private variable _count in the last line of the code.

The TypeScript compiler, therefore, is helping us to adhere to public and private accessors – by generating a compile error when we inadvertently break this rule.

Note

Remember, though, that these accessors are a compile-time feature only, and will not affect the generated JavaScript. You will need to bear this in mind if you are writing JavaScript libraries that will be consumed by third parties. The TypeScript compiler will also still generate the JavaScript output file, even if there are compile errors.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
3.20.225.142