Book Image

TypeScript 2.x By Example

By : Sachin Ohri
Book Image

TypeScript 2.x By Example

By: Sachin Ohri

Overview of this book

The TypeScript language, compiler, and open source development toolset brings JavaScript development up to the enterprise level. It allows you to use ES5, ES6, and ES7 JavaScript language features today, including classes, interfaces, generics, modules, and more. Its simple typing syntax enables building large, robust applications using object-oriented techniques and industry-standard design principles. This book aims at teaching you how to get up and running with TypeScript development in the most practical way possible. Taking you through two exciting projects built from scratch, you will learn the basics of TypeScript, before progressing to functions, generics, promises, and callbacks. Then, you’ll get to implement object-oriented programming as well as optimize your applications with effective memory management. You’ll also learn to test and secure your applications, before deploying them. Starting with a basic SPA built using Angular, you will progress on to building, maybe, a Chat application or a cool application. You’ll also learn how to use NativeScript to build a cool mobile application. Each of these applications with be explained in detail, allowing you to grasp the concepts fast. By the end of this book, you will have not only built two amazing projects but you will also have the skills necessary to take your development to the next level.
Table of Contents (11 chapters)

Type System

If there is one feature in TypeScript that stands out from its competitor, it is Type System, and how TypeScript uses types to help write better code. Types in TypeScript are one of the easiest features to understand and use, thus providing the maximum productivity boost for a new developer to TypeScript.

To develop any application with TypeScript, understanding types and their features is very important. Hence, we will look at the following topics in this section, which will then help us when we start working on our application:

  • Variables: We will start by looking at how to declare variables and constants in TypeScript. We will also look at the let and const keywords, which were introduced in ES 2015 to provide better scoping.
  • Types: We will then shift our focus to looking at types provided by TypeScript, which include both primitive and custom types.
  • Type inference: Then we will look at how TypeScript uses type inference to help identify the types.
  • Type compatibility: TypeScript has a feature wherein we can relate types based on members only; we will take a look at this with an example.
Types are a TypeScript concept and are not emitted in the transpiled JavaScript. They are only used at design and compile time.

Variables

If you have done any JavaScript programming, you will be aware of the var keyword for declaring variables. In ES 2015, a couple of new keywords were introduced to allow declaring variables, namely let and const. The var keyword allowed us to declare a variable, but it had its quirks with regard to how scoping and access to the variables worked; let tries to solve this problem.

The var keyword

Variables declared with the var keyword have a global scope in which they are declared; this means that they can be accessed by any function sharing the same scope. The following example shows the scope of the variable value in innerFunction:

Also, variables declared with the var keyword are subject to hoisting. This means that if we declare a variable at the end of a function, the runtime will hoist it to the top and we will not have any error if we would have used that variable before being declared. In the following example, we see an example of hoisting. Although the value variable was declared at line 9, we were able to access it at lines 3 and 6. When this function is executed, the JavaScript runtime hoists the variable declaration to the top of the function, and hence, is available at lines 3 and 6. The output of the following code will be 1 and 0 respectively:

Let and const keywords

The let and const keywords solve these problems by providing block scoping for the variables, and do not support hoisting. Block scoping means that the scope of a variable is restricted to the scope in which it is declared, which is normally defined by curly braces. So, if a variable is defined inside a loop or an if condition, that variable is not available outside this block, as seen in the following example. Line 6 throws an exception of x is not defined, but we do get the correct response of 10 on line 4:

This helps reduce errors in code because we make sure we are using the variables in the correct scope. Also, the let keyword does not support hoisting, which means that variables are not moved to the top at runtime. They are accessible only after they are declared.

The const keyword is another way to declare variables and follows the same scoping principles as the let keyword. The only difference between const and let is that the values declared with const cannot change once they are bound. So, if you know that the variable you are declaring cannot and should not be allowed to reassign, then declare it with const, else use the let keyword.

Types in TypeScript

The JavaScript language does not have a concept of explicitly defining types of variables which end up being one of the main reasons for errors at runtime. We can assign one type to a variable and then later assign a different type to the same variable. TypeScript, with its type annotation, allows us to assign types to each variable, function, object, or class. Types act as a contract, which that variable has to follow, and the TypeScript compiler makes sure that there are no deviations from this contract. TypeScript does this with static and dynamic type checking. Types provide a set of rules which the compiler needs to follow for that variable; these rules also allow an autosuggest feature in IDEs by providing the most relevant options available. For example, by having types, we can make sure that while calling a function, we are passing the right set of variables.

TypeScript is an optional statically typed language, which means that it is not mandatory to assign types to each variable or function. This is why every JavaScript code is a valid TypeScript code. TypeScript provides a way to infer types for the variable based on the value assigned to the variable or the code flow. TypeScript will try to infer the best possible type, and then have checks in place to make sure that all the further operations do not contradict with the type. TypeScript also provides an option to explicitly opt out of the type for a variable by assigning the any keyword. For a variable with type any, TypeScript does not perform any type checking. This feature allows us to have existing JavaScript migrated to TypeScript without any issues.

TypeScript emits JavaScript even if there are type errors at compile time, allowing us to progressively update the JavaScript code.

Type annotation

To specify a type to a variable or a function, TypeScript provides a syntax for defining a type preceded by a colon. Type annotation comes after the variable identifier. Types can be primitive types, or arrays, or complex types using classes and interfaces. The following example shows the basic syntax of defining types of variables and functions:

let num: number=42;
function example(name: string, age:number): number{
return 42;
}

In the first line, when defining a num variable, it's optional to assign a value as well. In the function example, we see that we can define the type of the input parameter, and also for the return value. This helps us in making sure that all function calls are maintaining the correct signature contract.

Primitive types

TypeScript primitive types relate very closely to the types in JavaScript and follow the same principles. The primitive types available in TypeScript are as follows.

Number

A number data type can contain a double precision 64-bit floating point value. In TypeScript, the number has the same meaning as in JavaScript and represents all numbers, including decimals and hexadecimal. The following is an example of a number variable:

let num:number = 42;
let decimal = 42.0;

String

The string data type represents textual data in UTF-16 format. To assign a string value to a variable, we use single (') or double (") quotes. We can also create a string that spans multiple lines, and is called a template string. The following is an example of declaring strings:

let firstName: string = 'John';
let templateHTML: string = `
<h1>Title</h1>`

In the preceding code, templateHTML is an example of using a template string. This is one of the features that is very frequently used in Angular to define inline templates for its components. We will see these when we start working on our application.

The strings also have an ability to add dynamic expression in its definition, as shown in the following example. The result variable will be Top 10 news feed from ESPN:

let news: string = "ESPN";
let count: number = 10;
let result: string = `Top ${count} news feed from ${news}.`
console.log(result);

The preceding code, when transpiled to JavaScript, produces the following output:

var news = "ESPN";
var count = 10;
var result = "Top " + count + " news feed from " + news + ".";
console.log(result);

Boolean

The boolean data type can be assigned a true or false value as shown in the following example:

let hasvalues: boolean = false;

Arrays

Like JavaScript, TypeScript has an array type to allow assignment of multiple values. The array is specified by adding a square bracket after the type. Every time a new value is added to an array, the compiler checks for type compatibility and alerts if there is a type mismatch. The following is an example of defining an array:

let scores:number[] = [10,20,30,40];

Arrays are accessed based on zero index, which means that the first element is at zeroth index, as shown here:

let scores:number[] = [10,20,30,40];
console.log(scores[0]);

The output of the console statement will be the value 10.

Tuples

Tuples can be seen as an advanced type of an array wherein we can have an array with elements not of the same type. For example, we can have any tuple that will have the first element of type string and the second element as a number, as shown here:

let details:[string, number];
details= ['John', 42];

The preceding code allows us to manage different data types in a single structure, which provides flexibility.

Any

The any keyword is a special type in TypeScript that allows us to opt out of type checking for that variable. This type is very useful when we are migrating the old JavaScript code to TypeScript. We can have the variables defined as any, and TypeScript will not perform any type checking on those. The following example shows the use of the any type:

let item: any;
item = 10;
item = 'John';
item = [10,20,30];

In the preceding code, the compiler does not complain when we assign a number, string, or array to the same variable.

It is recommended to not use any type in TypeScript code until and unless we are in the process of migrating the code from JavaScript. Having the any type takes away all the benefits of compile-time type checking from our code, which can result in unintended behavior.

Void

The void keyword is used to represent a scenario where there is no type. This is useful in the case of functions that do not return any value. These functions are annotated with void as a return type. In the following example, the doSomething function does not return any value, and hence has a void as return type:

function doSomething(num: number):void{
console.log(num);
}

Null and undefined

The null and undefined types are special types that can be assigned to any variable. They are not very useful on their own, as seen in the following example:

let value = null;
value = 42;

Here, the value variable is of the any type, because null is a subtype of all the types and the compiler assigns any to the variable.

Union types

Most of the time, we are aware of what type of data a specific variable can hold, and we can annotate the said variable with that type. This helps in type checking at compile time, and makes sure we don't misuse the variable. But there are times when a variable may not be confined to one specific type, but can have multiple types of values. This is common in scenarios where we are migrating JavaScript code, or using reference of JavaScript libraries.

TypeScript has the union type to solve this problem. A union type allows us to define a variable with multiple types; for example, a variable can have number and string types. This is achieved by using the pipe (|) symbol between the types, as shown here:

let data : string | number;
data = 10;
data = 'John';

Here, the data variable can hold both number and string, which allows us to have the flexibility to use both data types. The TypeScript compiler makes sure that it alerts us if we try to assign a type of value that was not defined.

Like union types, we have intersection types, which allow us to combine multiple types into one type. This is mostly used when we are using classes and interfaces for type declaration, hence we will look into this type when we discuss interfaces in the subsequent chapter.

Type inference

As we discussed in the Types in TypeScript section, TypeScript is an optional statically typed language, which means that TypeScript does provide types, but explicitly assigning types to variables is optional. TypeScript has an ability to infer types if not declared. Let's look at an example:

let firstName = "John";
firstName = 10;

In the line firstName = 10, the compiler alerts us with the error that the variable firstName is a string, and a number cannot be assigned to a string. How does TypeScript identity this mismatch? TypeScript uses type inference to identify the type for the firstName variable.

In this case, TypeScript identified based on the assignment of the value at the time of variable declaration. If we had not assigned "John" to the firstName variable, the type inferred by TypeScript would have been any, as in the example shown here:

let lastName;
lastName = 10;
lastName = 'jj';

This is called type inference through declaration. Another place where you can see type inference in action is the return type of a function. TypeScript looks at the code and, based on the code flow path, decides the best suitable type. The following is one such example:

function doSomething(num:number){
return "name";
}

In this case, TypeScript infers the return type as the string. Type inference is very useful in cases when we are working on legacy code of JavaScript; TypeScript will make sure to infer types for variables and functions based on the code flow.

Type checking

Once TypeScript has identified the types, it uses these for type checking in the program. TypeScript checks if we try to assign a type of value to a variable that contradicts the type defined for that variable, or when a function is called, TypeScript checks if we are passing correct types for the parameters and the return value is being assigned to the correct type of variable. The only exception to this type checking is the any keyword. If a variable or property is defined with the any keyword, the TypeScript compiler does not perform type checking for that variable.

Take a look at the following example of type checking:

let age:number;
age=10;
age="42"; //Compile Error: string can not be assigned to a number

In the preceding example, the TypeScript compiler alerts us on the last line with the message that 42 can not be assigned to a number, because although we are passing a number, we are passing it as the string. TypeScript, unlike JavaScript, does not coerce the types. TypeScript not only does type checking for primitive types, but also for arrays and custom types such as classes. We will look at an example of classes in the next chapter.

Apart from the types we have discussed in the preceding sections, there are other types in TypeScript as well, such as enum, generics, intersection types, and optional types. We will be looking into these types as and when we use them in our example application.