TypeScript for Angular 2 - Part 3 (Amazing TypeScript Features)

This is the third post of the TypeScript for Angular 2 series. If you have not looked Part 1 and Part 2, I do recommended you taking a look those two first to get brief understanding of TypeScript and its new features and syntaxes introduced by ES2015 and ES2016, which allows us to take full advantages of TypeScript in Angular 2.

After this post, we will have great understanding of all amazing features of TypeScript that is a big step to start writing Angular 2 applications.

Taking advantage of static typing

While writing JavaScript, we were only provided with some syntax highlighting and providing some basic autocompletion suggestions based on the sophisticated static analysis of our code by IDEs and text editors. However, the immediate preprocessing of TypeScript code allows to performs type checking and drops all the type annotations in order to provide valid JavaScript supported by modern web browsers.

So, let's learn some TypeScript types and their annotations.

Using explicit type definitions

In TypeScript, you can explicitly declare the type of the given variable just like Java and C++:

let foo: number = 35;

First, we define the variable named foo as scope variable with syntax. We also explicitly declare the type of foo to be number type and set value to 35.

The explicit type declaration forbids setting the wrong value type, which is perfectly valid in JavaScript:

let foo: number = 35;
foo = '35';

After we explicitly declare the variable foo as number, we cannot set its value to string value. TypeScript will show an error in compile time like following.

error TS2322: Type 'string' is not assignable to type 'number'.

The type any

We can use any keyword to declare variables that belong to the any type because all the types in TypeScript are subtypes of a type called any:

let foo: any;  
foo = { };  
foo = 'bar';  
foo += 35;  
console.log(foo); // "bar 35"  

TypeScript will not throw any error during compilation or runtime for this example. We use any keyword like dynamic typing for the variable foo that we defined. But it should be used with caution only when necessary.

TypeScript types except any

There are four other types in TypeScript, apart from the type any, which are: Primitive types, Union types, Object types and Type parameters.

Primitive types

Number, String, Boolean, Void, Null, Enum and Undefined fall into this category. We are already familiar with all these data types. I will skip primitive types and let's take a look at Union types.

Union types

They are one of the advanced types of TypeScript and powerful in expressing a value that can be one of several types. I will leave these types as well because this should be a post itself and contain lots of features that we need to learn. We can still develop Angular 2 applications with TypeScript better without knowing well enough of Union types.

So, let's continue to one of most important types of TypeScript, Object types, which contain two common types: Array and Function types.

Array types

TypeScript's arrays are just JavaScript arrays with a common element type, which means an array cannot contain different value types. In TypeScript, We can define different array types by using built-in types and custom types that we define as well. The following example is defining an array of numbers:

let primes: number[] = [];  
primes.push(10);  
primes.push(4);  

We can use the type any if we want an array that can have any type, which is similar to JavaScript arrays:

   let randomItems: any[] = [];
   randomItems.push(4);
   randomItems.push("foo");
   randomItems.push([]);
   randomItems.push({});

We can use all available JavaScript array methods, such as join, splice, push and etc,.. in TypeScript as well.

Function types

The function types are a set of all the functions with different signatures, including the different number of arguments, different arguments' types, or different types of the return result. Let's take a look how to define a function using old function expression and new arrow function syntax of ES2015.

// define a function using function expression
var isEven = function (n) {  
// body 
};

// define a function using function declaration
function isEven(n) {  
// body 
};

// define a function using new arrow function syntax 
var isEven = n => {  
// body 
};

TypeScript differs from JavaScript in defining the types of the function's arguments and the type of its return result as following.

/* defining types of a function arguments and return result */
// in arrow function syntax
let isEven: (n: number) => boolean = n => {  
   // body 
};

// in function declaration 
function isEven(n: number): boolean {  
   // body 
};

We can use void function if we want our function to do something instead of returning a result. For example:

let person = {  
     _name: null,
     setName(name: string): void {
     this._name = name;
  }
};

Type parameters

When we use type any in everywhere in order to get most flexibility, it is like we have forgotten the reason why we use TypeScript in Angular 2 -- static typing. We will lose the advantage of compile-time type checking if we use any where it should not be used like following example.

class Node {  
    value: any;
    left: Node;
    right: Node;
}
class BinarySearchTree {  
     private root: Node;
     insert(any: value): void { /* ... */ }
     remove(any: value): void { /* ... */ }
     exists(any: value): boolean { /* ... */ }
     inorder(callback: {(value: any): void}): void { /* ... */ }
}

In above snippet, we defined a class called Node, which has two child notes left and right, and a value of the type any. In both classes Node and BinarySearchTree, we use any in order to be able to store data of any type, which should not be used. Every-time we access the value property of the instances of the Node class, the static typing features of TypeScript provides are limited.

We can solve this by parameterising the classes we create with the type parameters by using generics:

 class Node<T> {
     value: T;
     left: Node<T>;
     right: Node<T>;
}

Node<T> means that Node class has a single type parameter called T that is used somewhere inside the class's definition. We can further implement Node class as following:

let numberNode = new Node<number>();  
let stringNode = new Node<string>();  
numberNode.right = new Node<number>();  
numberNode.value = 26;  
numberNode.value = "26"; // Type "string" is not assignable to type "number"  
numberNode.left = stringNode; // Type Node<string> is not assignable to type Node<number>  

By assigning single type to each individual nodes using the type parameters, we can immediately know if there is a type error in our code just in compile-time.

Less verbose code

This is the end of the types of the TypeScript. Now, let's take a look how to write less verbose code with type interface by some examples.

If a variable is declared with neither an explicit type declaration nor a value assigned to it, the compiler will set its type to any:

let apple;
apple = 39;
apple = "39";

The TypeScript complier will compile without any errors unless a value is assigned within its definition like following:

let apple = 39; apple = "39" // Type "string" is not assignable to type "number"

Becoming several expressions by type interface could also happen in declaring arrays.

let x = ["39", 39];

Then, TypeScript will set the type of x to any[] unless all included included falls into the same type:

let x = [39, null, 91];

The type of x will be number[] because the type Number is a subtype of Null.

Ambient type definitions

While we are taking full advantages of TypeScript static typing, we are losing the ability to use external libraries, such as jQuery, which is also written in JavaScript, altogether with with TypeScript in our Angular 2 applications. TypeScript awares of that and it solves by using Ambient type definitions that allows us to provide external type definitions of the existing JavaScript libraries.

Using predefined ambient type definitions

Fortunately, we dont have to define ambient type definitions for all JavaScript libraries and frameworks we use in our apps. The community have already published such definitions online resided at: DefinitelyTyped, which is the biggest repository for ambient type definitions. All definitions are managed by a tool called typings that can be installed using npm by following command:

npm install -g typings

This will install typings globally that make typings init command available from our Terminal.

In order to use ambient type definitions to your project, open your project location in Terminal and create typings.json by the following command:

typings init

This will create a file called typings.json, which defines the configuration of typings, and put all installed ambient typings in the directory ./typings by default.

If you want to use new type definitions, you can install them by their name using:

typings install [name] --ambient

Alternatively, you can add your ambient dependencies lists in your typings.json first and install them with typings install command just like in Angular 2 Developer Guide - TypeScript Configuration.

I would like to end this here and there is only one more part to go in our learning TypeScript for Angular 2. The last part, TypeScript for Angular 2 - Part 4 (Classes and Interfaces) will describe how to define classes and interfaces in TypeScript and how to use them.

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

Nay Win Myint

Founder and CEO of Pancasikha Music Streaming Provider, JavaScript full-stack and Android developer and Graphic designer.

Rangoon, Myanmar