Abstract: The new features in TypeScript 1.4 and 1.5 is bringing it closer to the ES6 and ES7 specifications. This article explores what's new in TypeScript 1.4 and 1.5

As JavaScript’s popularity continues to grow and a lot of exciting new features are added to its next versions, compile-to-JavaScript languages have a lot of challenge to face. TypeScript, the typed superset of JavaScript from Microsoft has taken up these challenges and continues adding more features to the language to make developers more productive. At the time of this writing, though the language supports comparatively lesser number of features of the next version of JavaScript , the team has promised that it will have full support for ES6 by the time TypeScript 2.0 is released. In addition, popularity of the language got a big push after the AngularJS team chose to use TypeScript as their language of development over AtScript.

This article is published from the DNC Magazine for .NET Developers and Architects. Download this magazine from here [PDF] or Subscribe to this magazine for FREE and download all previous and current editions

One of the key reasons why TypeScript started gaining a lot of popularity, is it stays true to ECMAScript standards. This focus on JavaScript makes the language look as close to JavaScript as possible and makes developers still appreciate the syntax of JavaScript. In the recent versions of TypeScript (1.4 and 1.5 beta), the language got some new features that loosens the type system to make it appear a bit more dynamic and even adds some new features of EcmaScript (ES) 6 & 7. We will explore these features in this article.

Using TypeScript 1.5 Beta

The latest version of Visual Studio 2015 CTP installs TypeScript 1.4 on the machine. Installer for TypeScript 1.5 is not yet available as of this writing. It is available through NPM. So you can install it globally using the following NPM command:

npm install –g typescript

If you have already installed an older version of TypeScript using NPM, you can update it using the following command:

npm update –g typescript

Union Types

Because of the optional type system in TypeScript, it was not so easy to define a function that takes different types of parameters across different invocations. It was also a challenge to write type definitions for such functions, as we needed to write a declaration for each possible invocation. TypeScript adds a new feature in 1.4 called Union Types. This feature allows us to specify multiple types for the same argument without having to write multiple declarations for the same function.

Let us say I have to write a function that accepts either a number, or an array of numbers and returns square of the number, or array of squares depending on the input passed in. Following is the function in TypeScript:

function square(nums: number | number[]): number | number[]{ if(typeof nums === 'number'){ return nums * nums; } else{ var squares = []; nums.forEach(num => { squares.push(num*num); }); return squares; } }

See the type of argument mentioned in the function. It says, the input can be either a number or, an array of numbers. Similarly, the function returns two types of values, so the return type is also specified in a similar way.

This feature eases the job of writing type declarations for existing libraries. For example, jQuery’s find() method can be called by passing a string selector, an element or a jQuery object to it. If you see the type declaration for this method in the Definitely Typed project, it has three declarations. They are:

find(selector: string): JQuery; find(element: Element): JQuery; find(obj: JQuery): JQuery;

Using the Union types, it can be replaced with a single declaration, as shown here:

find(seo: string | Element | JQuery): JQuery;

Type Aliases

While writing considerably larger apps using TypeScript, it is natural to have the application divided into several modules and each module depending on other modules to achieve its functionality. If a class or an interface defined in a module has to be used a number of times in the importing module, we will have to keep referring to the types using module’s reference again and again. Also some types imported from another module may have longer names, or you may want to refer to these types with different names. Thankfully, TypeScript 1.4 adds a feature called Type Aliases, which allows us to create alias names for a type.

Say I have a module with some interfaces representing contracts to define different types of cars. To keep it simple, I included two types of cars here, cheap and costly. Following is the module:

module Cars{ interface ICar{ manufacturer: string; price: number; drive(speed: number); } export interface ICheapCar extends ICar { mileage: number; } export interface ICostlyCar extends ICar { length: number; width: number; } }

The only way we know to refer to these interfaces, is by using the module name.

var car1 : Cars.ICheapCar; var car2 : Cars.ICostlyCar;

Using this syntax over and over to refer to these types would tire us. So, let’s create aliases. Following snippet creates the aliases:

type IEconomicCar = Cars.ICheapCar; type ILuxuriousCar = Cars.ICostlyCar;

Now you can use the alias names to refer to the interfaces.

var car1 : IEconomicCar; var car2 : ILuxuriousCar;

It is also possible to create types on primitive types. Here are some examples:

type setOfChars = string; type numericValues = number;

We can create aliases on mixed-generic types as well. Here’s an example:

type CarCollection = Array<IEconomicCar | ILuxuriousCar>;

The type CarCollection can be used to store a list of objects of both IEconomicCar and ILuxuriousCar types.

The typeof and instanceof operators can be applied on variables defined using type aliases to check their type before performing an operation. For example, the following snippet defines two classes and a collection to hold objects of these types:

class Farmer{ startFarming(){ console.log("Started. Don't disturb me for 3 hours from now."); } } class Carpenter{ buildADoor(){ console.log("Will start today. Meet me after 10 days."); } } type WorkersCollectionType = Array<Farmer | Carpenter>; var workers: WorkersCollectionType = []; workers.push(new Carpenter()); workers.push(new Farmer()); for(var count = 0; count<workers.length; count ++){ if(workers[count] instanceof Carpenter){ workers[count].buildADoor(); } else{ workers[count].startFarming(); } }

The loop that iterated over the items in the collection checks for the type of the instance, before it performs an operation.

Better Generics and Generic Type Inference

Because of alias types, arrays infer types from the value assigned to them during declaration. For example, consider the following snippet:

var arr = [10, new Carpenter()]; arr.push(161); arr.push("Ravi"); //not allowed arr.push(new Carpenter());

As the array is initialized with a number and an object of Carpenter type, type of the variable is assigned as an array of a union type. So the first statement is similar to:

var arr2:Array<number | Carpenter> = [10, new Carpenter()]

As string is not compatible with any of these types, an attempt to insert a string value into the array results in an error.

Generics have been made stricter and they restrict assigning values of incompatible types on two assignment targets declared using the same generic notation. Following is an example of strict generics:

function add<T>(first: T, second: T) : T{ if(typeof first === 'number' || typeof first === 'string') { return first + second; } return null; } console.log(add(1, 25)); //26 console.log(add("firstName","lastName")); //firstNamelastName console.log(add([1,2,3],[3,4,5])); //null console.log(add(1, "Ravi")); //Error

Const Enums

We use enums to store a list of fixed values and these values are collectively used to represent a set of values. On compilation, TypeScript generates an object for the enum and the values are assigned to properties in the object.

When marked with the keyword const, the compiler doesn’t create an object for the enum. So it is not allowed to access the const enum as an object in TypeScript. We can use the values alone and the compiler replaces all usage occurrences with their corresponding values.

const enum Days {Sunday, Monday, Tuesday, Wednesday, Thursday, Friday, Saturday}; var days = Days; //not allowed console.log(Days.Monday); //0

ES6 Features

As I stated in the introduction of my first article on ES6, ES6 got a lot of features from some compile-to-JavaScript languages and from popular libraries. ES6 got some of its features like classes, arrow functions, some part of module system and a couple of others from TypeScript as well. So some of the ES6 features are already available in TypeScript. The team started implementing features of ES6 into the language and TypeScript 1.5 is going to have a decent support for ES6.

ES6 output mode

By default, TypeScript code gets converted to ES5 or, ES3 version of JavaScript. Now we can transpile TypeScript code to ES6 code using a compilation option. Use the following command:

tsc --target ES6 file.ts

Let and Const

The JavaScript we use today doesn’t have block level scoping. Any variable declared using the var keyword at any level in a function, is hoisted at the beginning of the function. ES6 adds block level scoping by introducing a new keyword, let. TypeScript has got support for this keyword now. The let keyword can be used to declare intermediate variables that store temporary values, like counter in for loop. Here is an example:

for(let c = 0; c< 10; c++){ console.log(c*c); }

The const keyword in ES6 is used to define scoped constants. TypeScript now supports this keyword. Any attempt to reassign value of a constant would result in an error. The following function uses the const keyword:

function findArea(radius: number): number{ const pi=3.4159; return pi * radius * radius; } console.log(findArea(20));

Template Strings

Appending values to strings in JavaScript has never been enjoyable. ES6 adds support for template strings to make it easier to add values of variables to a string and to easily assign multi-line values to strings. TypeScript 1.4 adds this feature to the language. The following snippet uses this feature:

var person={ firstName:'Ravi', lastName:'Kiran', occupation:'Author' }; console.log(`${person.firstName} ${person.lastName} is a/an ${person.occupation}.`); var template=`<div> <span>Some text goes here...</span> </div>`;

Destructuring

Destructuring is a feature added to JavaScript in ES6 that saves a lot of time in extracting values out of arrays and objects. It defines a shorter way of assigning the values from arrays and objects into variables. TypeScript 1.5 adds the support of destructuring to the language and it gets transpiled to its best possible alternative in the target version.

Following are a couple of examples of destructuring:

var numbers=[20, 30, 40, 50]; var [first, second, third ] = numbers; //first: 20, third: 40 var topic = {name:'ECMAScript 6', comment: 'Next version of JavaScript', browserStatus: { chrome: 'partial', opera:'partial', ie: 'very less', ieTechPreview: 'partial' }}; var {name, browserStatus:{opera}} = topic; //name: 'ECMAScript 6', opera: 'partial'

Modules

Support of modules is one of the most important features added to ES6. Addition of modules makes it easier to structure the code and manage the dependencies easily without need of an external library. As the feature is not yet implemented by browsers, we need to rely on an existing module system like AMD or CommonJS today to manage JavaScript dependencies in production environments. Though TypeScript has its own module system, it now embraces the module system of ECMAScript 6 and provides a way to transpile the ES6 modules into either AMD or, CommonJS system.

If you are not already familiar with the syntax and usage of ES6 modules, check the article on ES6 modules on DotnetCurry.

Consider the following code. It is a piece of TypeScript code using the export statement of ES6 to export objects out of the module.

class Employee{ id: number; name: string; dob:Date; constructor(id, name, dob){ this.id = id; this.name=name; this.dob= dob; } getAge(){ return (new Date()).getFullYear() - this.dob.getFullYear(); } } var [x, y] = [10, 20]; function getEmployee(id, name, dob){ return new Employee(id, name, dob); } export {Employee, getEmployee};

Say, the file is saved as employee.ts. Following command would transpile the module into a CommonJS module:

tsc employee.ts --module commonjs --target ES5

Now you can run the browserify command over the generated file and load the file in a browser . To use browserify, you need to install the NPM package of browserify globally. The following command does this for us:

npm install -g browserify

Here is the browserify command to create a bundle file containing the above file:

browserify employee.js > bundle.js

The bundle file is a self-contained file and it can be loaded into the browser without importing any other external scripts. You may check the official site of browserify if you want to learn more.

As you can see, the command accepts the module flag in addition to the target flag discussed earlier. Using this flag, the TypeScript file can be transpiled into either a CommonJS module or, an AMD module.

Decorators (ES7)

Decorators are not added to the specification of ES6; instead they are a part of the ES7 spec. Using decorators, a JavaScript object can be extended declaratively. This feature is already used in Angular 2.0 and in Aurelia. These frameworks use this feature extensively to achieve things like Dependency Injection, making a class a component, to make a field bindable, and many more.

Though the name sounds like extra burden, defining and using a decorator is fairly simple. A decorator is a function that accepts the object to be decorated, name of the property and object descriptor. It has to be applied on a target using the “at the rate” (@) symbol. Following is the signature of a decorator function:

function decoratorFunc(target, name, descriptor){ //body of the function }

The following snippet defines and uses a decorator:

function nonEnumerable(target, name, descriptor){ descriptor.enumerable = false; return descriptor; } class Person { fullName: string; @nonEnumerable get name() { return this.fullName; } set name(val) { this.fullName = val; } get age(){ return 20; } } var p = new Person(); for(let prop in p){ console.log(prop); }

As the property name has been made non enumerable using a decorator, the “for…in” loop prints the property age alone on the console.

Decorator Metadata

Metadata Reflection API is another proposed feature for ES7 (link to proposal). This API is designed to be used along with the decorators to implement features like Dependency Injection, perform runtime type assertions and mirroring. The feature is already in use in Angular 2 for DI and to declare components.

To use this feature, we need the polyfill of Reflection API. It can be installed using the following NPM command:

npm install reflect-metadata

We can either import this library into the TypeScript file using the ES6 module syntax or, we may even load this script in the browser before the script using it, loads to make the API available.

Now we can start defining the metadata annotations using this API and start using them. Following is a decorator that uses the metadata API:

function Inputs(value: Array<string>) { return function (target: Function) { Reflect.defineMetadata("InputsIntoClass", value, target); } }

This is a simple decorator that accepts the Type of data passed into a class or, a function. One can extend this idea to create a dependency injection system.

The following class uses this decorator and passes the metadata:

@Inputs(["Employee"]) class MyClass { emp: Employee; constructor(e: Employee){ this.emp = e; } }

Now we can get the value of metadata applied on this class using the metadata read APIs. The following snippet reads the metadata of this class:

let value: Array<string> = Reflect.getMetadata("InputsIntoClass", MyClass); console.log(value);

The console.log statement in the above snippet prints value of the metadata passed into the decorator, which is an array containing a single value in this case. You can read this information to create an abstraction to instantiate the class.

Conclusion

The TypeScript team is putting together a lot of work to make the language better for larger applications and by keeping it as close to JavaScript as possible. The new features and the support for ES6 helps to keep the language relevant in modern JavaScript world too. The final version of TypeScript 1.5 may include a couple of additional features and the next version will have support for async/await (part of ES7 spec). We will keep you updated with these features as they release.

This article has been editorially reviewed by Suprotim Agarwal.

C# and .NET have been around for a very long time, but their constant growth means there’s always more to learn. We at DotNetCurry are very excited to announce The Absolutely Awesome Book on C# and .NET. This is a 500 pages concise technical eBook available in PDF, ePub (iPad), and Mobi (Kindle). Organized around concepts, this Book aims to provide a concise, yet solid foundation in C# and .NET, covering C# 6.0, C# 7.0 and .NET Core, with chapters on the latest .NET Core 3.0, .NET Standard and C# 8.0 (final release) too. Use these concepts to deepen your existing knowledge of C# and .NET, to have a solid grasp of the latest in C# and .NET OR to crack your next .NET Interview. Click here to Explore the Table of Contents or Download Sample Chapters!