TypeScript for Angular 2 - Part 2 (Changes in ES2015 & ES2016)

This is the followed up post of TypeScript for Angular 2 - Part 1 that will explains about bigger changes in ES2015 and ES2016 in order to understand TypeScript's syntax and features.

As TypeScript is a superset of JavaScript, it is better introducing some of the new standards changed in ES2015 and ES2016, which allows you to take full advantages of TypeScript features you are going to need in Angular 2 applications development. However, I am not going details on these two as our main point is to get to know their major features in relation to TypeScript for Angular 2.

So, let's begin...

If you want to learn full details about syntax and features of ES6, i do recommend ES6 & Beyond book, which is the sixth book in You Don't Know JS series, written by Kyle Simpson.

ES2015 Arrow Functions

Instead of existing first class functions, ES2015 introduced a new syntax to define anonymous functions called the arrow function syntax. We can redefine our anonymous functions like following example.

/* ES2015 Arrow Functions Example 1 */ 
// Old syntax
var even = [1, 4, 10, 23, 30]  
     .filter (function(value) {
         return value % 2 === 0
});

// New ES2015 syntax
var even1 = [1, 4, 10, 23, 30]  
     .filter (value => !(value % 2));

console.log(`Example 1 ans: even ${even}`);  
console.log(`Example 1 ans: even1 ${even1}`);  

You will get the same result of the array [ 4, 10, 30 ] from both old and new syntax functions which contains even numbers only.

Another one of the most important features of Arrow Functions, which is extremely useful in Angular 2, is that they keep the context (this) from the surrounding code. Let's see one more example.

/* ES2015 Arrow Functions Example 2 */ 
function MyComponent() {  
   this.page = 102;
      setTimeout(() => {
        this.page += 1;
          console.log(`Example 2 ans: ${this.page}`);
      }, 100);
}
new MyComponent(); // 103 in 100ms.  

In this example, the arrow function keeps the context (this) of the whole function, which has been invoked by the new operator, till into the callback of setTimeout, and then print 103 as a result.

In Angular 2, this allows components binding to its properties through out its code without using scope or explicit calling to the $digest loop.

Classes

JavaScript implements the OO paradigm unlike other programming languages, such as Java, C#, C++. We can instantiate objects using the object literal syntax or functions like constructor functions as it has a prototyped-based object-oriented programming model. We can also implements inheritance using a mechanism of Javascript called prototype chain.

However, those syntaxes and processes of implementing the OO paradigm became confusing for Javascript beginners who are not sure how to us this properly. So, ES2016 adds some extra syntax to ES2015 classes to consolidate the JavaScript class declaration.

/* ES2016 Classes Example 3 */ 
class Human {  
     static totalPeople = 0;
     _name; // ES2016 property declaration syntax
     constructor(name) {
       this._name = name;
       Human.totalPeople += 1;
     }
     get name() { return this._name; }
     set name(val) {  this._name = val; }
     talk() { return `Hi, I/'m ${this.name}!`;  } 
}
class Developer extends Human {  
     _languages; // ES2016 property declaration syntax
     constructor(name, languages) {
       super(name);
       this._languages = languages;
     }
     get languages() { return this._languages;  }
    talk() { return `${super.talk()} And I know ${this.languages.join(',')}.`;  } 
}

var human = new Human("foobar");  
var dev = new Developer("bar", ["JavaScript"]);  
console.log(`Example 3 ans: ${dev.talk()}`);

In this ES2016 class example, we defined a class called Human, which has a single property that has been set value when the object is instantiated in its constructor. We added another class Developer that extends Human class. Then it passes the new name bar to its parent class when instantiated. Then we print back by using talk function from parent class by adding its new property language.

This shows how ES2016 OO paradigm syntaxes become more clear and robust to use in Angular 2. We will definitely uses classes in Angular 2 to construct app components, directives, services and pipes. As I mention in previous post, you can still use the alternative ES2015 syntax because there would be no significant difference between two syntaxes once the TypeScript is complied.

Let for block scope Variables

JavaScript uses functions instead of blocks in defining the scope of the variables. This means that the value of variables defined in different functions cannot access each other even though they are defined in the same block. This is the common problem that we used to encounter in writing JavaScript like the following example.

/* ES2016 Classes Example 4 */ 
// Old Syntax
var fns = [];  
for (var i = 0; i < 5; i += 1) {  
    fns.push(function() { 
    console.log(`Example 4 ans: old syntax -  ${i}`); }) 
}
fns.forEach(fn => fn()); // log 5 times => only number 5  

ES2015 added a new syntax let to define the variables with block-scope visibility. The remaining will be the same and it changes uses the keyword let instead of var.

var fns = [];  
for (let i = 0; i < 5; i += 1) {  
    fns.push(function() { 
    console.log(`Example 4 ans: new syntax -  ${i}`); }) 
}
fns.forEach(fn => fn()); // log 5 times => 0 to 4  

Meta-programming with ES2016 decorators

ES2016 Decorators are generally just another syntax sugar by allowing us to do a lot of fancy things by changing the behaviour of our programs. We can improve the readability of the code by making a set of predefined decorators, which can be annotating a given methods or properties as deprecated or read-only. Angular 2 will commonly use ES2016 decorators to define components, directives, pipes and to take advantage of the dependency injection mechanism of the framework. Let's take a look an example of using the decorators defined by Angular 2.

@Component({
     selector: 'app',
     providers: [NamesList],
     templateUrl: './app.html',
     directives: [RouterOutlet, RouterLink]
})
@RouteConfig([
     { path: '/', component: Home, name: 'home' },
     { path: '/about', component: About, name: 'about' }
])
export class App {}  

ES2015 module system

The lack of a module system in JavaScript language was another problem along the years and the module patterns developed by the community were more like workarounds rather than real solutions. Based on CommonJS and Asynchronous Module Definition, which are widely used for handling of circular dependencies, asynchronous module loading, and so on, TC39 took all the best parts of the existing module system and introduced on a language level. In ES2015, two APIs, Declarative API and Imperative API, were introduced to define and consume modules. Let's take a look at a simple example below:

/* ES2015 module system example */
// typeScriptForAngular2/math.ts
export function square(x) {  
    return Math.pow(x, 2);
};
export function log10(x) {  
    return Math.log10(x);
};
export const PI = Math.PI;  

In this example, we define a simple ES2015 module in the file math.ts. Then we defined and exported the functions square and log10 using export keyword, which is nothing more than prefixing the function's definitions. Alternatively, we can skip those duplicate export keyword and export entire functionality in the end:

/* ES2015 module system example */
// typeScriptForAngular2/math2.ts
function square(x) {  
    return Math.pow(x, 2);
};
function log10(x) {  
    return Math.log10(x);
};
const PI = Math.PI;  
export { square, log10, PI };  

This math2.ts will give the same result as math.ts while it exports all functions on the last line with an enhanced object literal syntax. Then we will consume this module like following.

/* ES2015 module system example */
// typeScriptForAngular2/modularSystem.ts
import {square, log10} from './math2';

console.log(square(2)); // 4  
console.log(log10(10)); // 1  

We imported the required functions, square and log10, first using the relative path of the current file and then all functionalities become available in the modularSystem.ts file.

Moreover, we can also import entire module using *:

import * from './math2';

ES2015 module syntax has implicit asynchronous behaviour. This means that if we want to use module A, which depends on modules B and C, the JavaScript module loader would need to load both modules B and C before being able to invoke any of the logic that resides in module A because of the dependencies we have.

ES2015 module loader

The module loader API, which is a new version of the standard that defines a programmatic API to work with modules, allows us to define and import modules or configure the module loading.

Suppose that we have a file app.js containing following module definition:

import { square } from './math';  
export function main() {  
    console.log(square(2)); // 4
}

Thank to ES2015 module loader, we can programmatically load the app module and invoke its main function from the file init.js using:

System.import('./app')  
.then(app => {
    app.main();
}).catch(error => {
    console.log('Terrible error happened', error);
});

In this example, the import method of the global object System allows us to import modules using their identifier. The import of app.js by System.import returns a promise that could be resolved on success or rejected in case of an error. We will get the app module once the promise is resolved as the first parameter of the callback passed to then. In case of rejection, the first parameter of the callback registered is the error that will caught in the catch method.

There are two more parts to go in our learning of TypeScript for Angular 2. In this part, we have learnt the significant relations between TypeScript and ES2015, ES2016 that allows us to take full advantages of Angular 2. The last two parts, TypeScript for Angular 2 - Part 3 and TypeScript for Angular 2 - Part 4 will describe all the amazing features of TypeScript itself, classes and interfaces.

Thank you for reading and sharing would be my pleasure if you enjoy this post. Cheers.

Nay Win Myint

A JavaScript full-stack web developer, also interested in Android application development and Graphic design.

United Kingdom