Learning TypeScript

4.3 (4 reviews total)
By Remo H. Jansen
  • Instant online access to over 7,500+ books and videos
  • Constantly updated with 100+ new titles each month
  • Breadth and depth in over 1,000+ technologies
  1. Introducing TypeScript

About this book

TypeScript is an open source and cross-platform typed superset of JavaScript that compiles to plain JavaScript that runs in any browser or any host. It allows developers to use the future versions of JavaScript (ECMAScript 6 and 7) today. TypeScript adds optional static types, classes, and modules to JavaScript, to enable great tooling and better structuring of large JavaScript applications.

This book is a step-by-step guide that will get you started with TypeScript with the help of practical examples. You start off by understanding the basics of TypeScript. Next, automation tools like Grunt are explained followed by a detailed description of function, generics, callbacks and promises. After this, object-oriented features and the memory management functionality of TypeScript are explained. At the end of this book, you will have learned enough to implement all the concepts and build a single page application from scratch.

Publication date:
September 2015
Publisher
Packt
Pages
368
ISBN
9781783985548

 

Chapter 1. Introducing TypeScript

This book focuses on TypeScript's object-oriented nature and how it can help you to write better code. Before diving into the object-oriented programing features of TypeScript, this chapter will give you an overview of the history behind TypeScript and introduce you to some of the basics.

In this chapter, you will learn about the following concepts:

  • The TypeScript architecture

  • Type annotations

  • Variables and primitive data types

  • Operators

  • Flow control statements

  • Functions

  • Classes

  • Interfaces

  • Modules

 

The TypeScript architecture


In this section, we will focus on the TypeScript's internal architecture and its original design goals.

Design goals

In the following points, you will find the main design goals and architectural decisions that shaped the way the TypeScript programming language looks like today:

  • Statically identify JavaScript constructs that are likely to be errors. The engineers at Microsoft decided that the best way to identify and prevent potential runtime issues was to create a strongly typed programming language and perform static type checking at compilation time. The engineers also designed a language services layer to provide developers with better tools.

  • High compatibility with the existing JavaScript code. TypeScript is a superset of JavaScript; this means that any valid JavaScript program is also a valid TypeScript program (with a few small exceptions).

  • Provide a structuring mechanism for larger pieces of code. TypeScript adds class-based object orientation, interfaces, and modules. These features will help us structure our code in a much better way. We will also reduce potential integration issues within our development team and our code will become more maintainable and scalable by adhering to the best object-oriented principles and practices.

  • Impose no runtime overhead on emitted programs. It is common to differentiate between design time and execution time when working with TypeScript. We use the term design time code to refer to the TypeScript code that we write while designing an application; we use the terms execution time code or runtime code to refer to the JavaScript code that is executed after compiling some TypeScript code.

    TypeScript adds features to JavaScript but those features are only available at design time. For example, we can declare interfaces in TypeScript but since JavaScript doesn't support interfaces, the TypeScript compiler will not declare or try to emulate this feature in the output JavaScript code.

    The Microsoft engineers provided the TypeScript compiler with mechanisms such as code transformations (converting TypeScript features into plain JavaScript implementations) and type erasure (removing static type notation) to generate really clean JavaScript code. Type erasure removes not only the type annotations but also all the TypeScript exclusive language features such as interfaces.

    Furthermore, the generated code is highly compatible with web browsers as it targets the ECMAScript 3 specification by default but it also supports ECMAScript 5 and ECMAScript 6. In general, we can use the TypeScript features when compiling to any of the available compilation targets, but there are some features that will require ECMAScript 5 or higher as the compilation target.

  • Align with the current and future ECMAScript proposals. TypeScript is not just compatible with the existing JavaScript code; it will also potentially be compatible with future versions of JavaScript. The majority of Typescript's additional features are based on the future ECMAScript proposals; this means many TypeScript files will eventually become valid JavaScript files.

  • Be a cross-platform development tool. Microsoft released TypeScript under the open source Apache license and it can be installed and executed in all major operating systems.

TypeScript components

The TypeScript language is internally divided into three main layers. Each of these layers is, in turn, divided into sublayers or components. In the following diagram, we can see the three layers (green, blue, and orange) and each of their internal components (boxes):

Note

In the preceding diagram, the acronym VS refers to Microsoft's Visual Studio, which is the official integrated development environment for all the Microsoft products (including TypeScript). We will learn more about this and the other IDEs in the next chapter.

Each of these main layers has a different purpose:

  • The language: It features the TypeScript language elements.

  • The compiler: It performs the parsing, type checking, and transformation of your TypeScript code to JavaScript code.

  • The language services: It generates information that helps editors and other tools provide better assistance features such as IntelliSense or automated refactoring.

  • IDE integration: In order to take advantages of the TypeScript features, some integration work is required to be done by the developers of the IDEs. TypeScript was designed to facilitate the development of tools that help to increase the productivity of JavaScript developers. As a result of these efforts, integrating TypeScript with an IDE is not a complicated task. A proof of this is that the most popular IDEs these days include a good TypeScript support.

    Note

    In other books and online resources, you may find references to the term transpiler instead of compiler. A transpiler is a type of compiler that takes the source code of a programming language as its input and outputs the source code into another programming language with more or less the same level of abstraction.

We don't need to go into any more detail as understanding how the TypeScript compiler works is out of the scope of this book; however, if you wish to learn more about this topic, refer to the TypeScript language specification, which can be found online at http://www.typescriptlang.org/.

 

TypeScript language features


Now that you have learned about the purpose of TypeScript, it's time to get our hands dirty and start writing some code.

Before you can start learning how to use some of the basic TypeScript building blocks, you will need to set up your development environment. The easiest and fastest way to start writing some TypeScript code is to use the online editor available on the official TypeScript website at http://www.typescriptlang.org/Playground, as you can see in the following screenshot:

In the preceding screenshot, you will be able to use the text editor on the left-hand side to write your TypeScript code. The code is automatically compiled to JavaScript and the output code will be inserted in the text editor located on the right-hand side of the screen. If your TypeScript code is invalid, the JavaScript code on the right-hand side will not be refreshed.

Alternatively, if you prefer to be able to work offline, you can download and install the TypeScript compiler. If you work with Visual Studio, you can download the official TypeScript extension (version 1.5 beta) from https://visualstudiogallery.msdn.microsoft.com/107f89a0-a542-4264-b0a9-eb91037cf7af. If you are working with Visual Studio 2015, you don't need to install the extension as Visual Studio 2015 includes TypeScript support by default.

If you use a different code editor or you use the OS X or Linux operating systems, you can download an npm module instead. Don't worry if you are not familiar with npm. For now, you just need to know that it stands for Node Package Manager and is the default Node.js package manager.

Note

There are TypeScript plugins available for many popular editors such as Sublime https://github.com/Microsoft/TypeScript-Sublime-Plugin and Atom https://atom.io/packages/atom-typescript.

In order to be able to use npm, you will need to first install Node.js in your development environment. You will be able to find the Node.js installation files on the official website at https://nodejs.org/.

Once you have installed Node.js in your development environment, you will be able to run the following command in a console or terminal:

npm install -g typescript

OS X users need to use the sudo command when installing global (-g) npm packages. The sudo command will prompt for user credentials and install the package using administrative privileges:

sudo npm install -g typescript

Create a new file named test.ts and add the following code to it:

var t : number = 1;

Save the file into a directory of your choice and once you have saved the file open the console, select the directory where you saved the file, and execute the following command:

tsc test.ts

The tsc command is a console interface for the TypeScript compiler. This command allows you to compile your TypeScript files into JavaScript files. The compiler features many options that will be explored in the upcoming chapters of this book.

In the preceding example, we used the tsc command to transform the test.ts file into a JavaScript file.

If everything goes right, you will find a file named test.js in the same directory in which the test.ts file was located. Now, you know how to compile your TypeScript code into JavaScript and we can start learning about the TypeScript features.

Note

You will be able to learn more about editors and other tools in Chapter 2, Automating Your Development Workflow.

Types

As we have already learned, TypeScript is a typed superset of JavaScript. TypeScript added optional static type annotations to JavaScript in order to transform it into a strongly typed programming language. The optional static type annotations are used as constraints on program entities such as functions, variables, and properties so that compilers and development tools can offer better verification and assistance (such as IntelliSense) during software development.

Strong typing allows the programmer to express his intentions in his code, both to himself and to others in the development team.

Typescript's type analysis occurs entirely at compile time and adds no runtime overhead to program execution.

Optional static type notation

The TypeScript language service is really good at inferring types, but there are certain cases where it is not able to automatically detect the type of an object or variable. For these cases, TypeScript allows us to explicitly declare the type of a variable. The language element that allows us to declare the type of a variable is known as optional static type notation. For a variable, the type notation comes after the variable name and is preceded by a colon:

var counter;               // unknown (any) type
var counter = 0;           // number (inferred)
var counter : number;      // number
var counter : number = 0;  // number

As you can see, the type of the variable is declared after the name, this style of type notation is based on type theory and helps to reinforce the idea of types being optional. When no type annotations are available, TypeScript will try to guess the type of the variable by examining the assigned values. For example, in the second line in the preceding code snippet, we can see that the variable counter has been identified as a numeric variable because the numeric value 0 was assigned as its value. This process in which types are automatically detected is known as Type inference, when a type cannot be inferred the especial type any is used as the type of the variable.

Variables, basic types, and operators

The basic types are the Boolean, number, string, array, void types, and all user defined Enum types. All types in TypeScript are subtypes of a single top type called the Any type. The any keyword references this type. Let's take a look at each of these primitive types:

Data Type

Description

Boolean

Whereas the string and number data types can have a virtually unlimited number of different values, the Boolean data type can only have two. They are the literals true and false. A Boolean value is a truth value; it specifies whether the condition is true or not.

var isDone: boolean = false;

Number

As in JavaScript, all numbers in TypeScript are floating point values. These floating-point numbers get the type number.

var height: number = 6;

String

You use the string data type to represent text in TypeScript. You include string literals in your scripts by enclosing them in single or double quotation marks. Double quotation marks can be contained in strings surrounded by single quotation marks, and single quotation marks can be contained in strings surrounded by double quotation marks.

var name: string = "bob";
name = 'smith';

Array

TypeScript, like JavaScript, allows you to work with arrays of values. Array types can be written in one of the two ways. In the first, you use the type of the elements followed by [] to denote an array of that element type:

var list:number[] = [1, 2, 3];

The second way uses a generic array type, Array:

var list:Array<number> = [1, 2, 3];

Enum

An enum is a way of giving more friendly names to sets of numeric values. By default, enums begin numbering their members starting at 0, but you can change this by manually setting the value of one to its members.

enum Color {Red, Green, Blue};
var c: Color = Color.Green;

Any

The any type is used to represent any JavaScript value. A value of the any type supports the same operations as a value in JavaScript and minimal static type checking is performed for operations on any values.

var notSure: any = 4;
notSure = "maybe a string instead";
notSure = false; // okay, definitely a boolean

The any type is a powerful way to work with existing JavaScript, allowing you to gradually opt in and opt out of type checking during compilation. The any type is also handy if you know some part of the type, but perhaps not all of it. For example, you may have an array but the array has a mix of different types:

var list:any[] = [1, true, "free"];
list[1] = 100;

Void

The opposite in some ways to any is void, the absence of having any type at all. You will see this as the return type of functions that do not return a value.

function warnUser(): void {
    alert("This is my warning message");
}

JavaScript's primitive types also include undefined and null. In JavaScript, undefined is a property in the global scope that is assigned as a value to variables that have been declared but have not yet been initialized. The value null is a literal (not a property of the global object). It can be assigned to a variable as a representation of no value.

var TestVar;           // variable is declared but not initialized
alert(TestVar);        // shows undefined 
alert(typeof TestVar); // shows undefined

var TestVar = null;    // variable is declared and value null is assigned as value
alert(TestVar);        // shows null 
alert(typeof TestVar); // shows object

In TypeScript, we will not be able to use null or undefined as types:

var TestVar : null;      // Error, Type expected
var TestVar : undefined; // Error, cannot find name undefined

Since null or undefined cannot be used as types, both the variable declarations in the preceding code snippet are invalid.

Var, let, and const

When we declare a variable in TypeScript, we can use the var, let, or const keywords:

var mynum : number = 1;
let isValid : boolean = true;
const apiKey : string = "0E5CE8BD-6341-4CC2-904D-C4A94ACD276E";

Variables declared with var are scoped to the nearest function block (or global, if outside a function block).

Variables declared with let are scoped to the nearest enclosing block (or global if outside any block), which can be smaller than a function block.

The const keyword creates a constant that can be global or local to the block in which it is declared. This means that constants are block scoped. You will learn more about scopes in Chapter 5, Runtime.

Note

The let and const keywords have been available since the release of TypeScript 1.4 but only when the compilation target is ECMAScript 6. However, they will also work when targeting ECMAScript 3 and ECMAScript 5 once TypeScript 1.5 is released.

Union types

TypeScript allows you to declare union types:

var path : string[]|string;
path = '/temp/log.xml';
path = ['/temp/log.xml', '/temp/errors.xml'];
path = 1; // Error

Union types are used to declare a variable that is able to store a value of two or more types. In the preceding example, we have declared a variable named path that can contain a single path (string), or a collection of paths (array of string). In the example, we have also set the value of the variable. We assigned a string and an array of strings without errors; however, when we attempted to assign a numeric value, we got a compilation error because the union type didn't declare a number as one of the valid types of the variable.

Type guards

We can examine the type of an expression at runtime by using the typeof or instanceof operators. The TypeScript language service looks for these operators and will change type inference accordingly when used in an if block:

var x: any = { /* ... */ };
if(typeof x === 'string') {
  console.log(x.splice(3, 1)); // Error, 'splice' does not exist on 'string'
}
// x is still any
x.foo(); // OK

In the preceding code snippet, we have declared an x variable of type any. Later, we check the type of x at runtime by using the typeof operator. If the type of x results to be string, we will try to invoke the method splice, which is supposed to a member of the x variable. The TypeScript language service is able to understand the usage of typeof in a conditional statement. TypeScript will automatically assume that x must be a string and let us know that the splice method does not exist on the type string. This feature is known as type guards.

Type aliases

TypeScript allows us to declare type aliases by using the type keyword:

type PrimitiveArray = Array<string|number|boolean>;
type MyNumber = number;
type NgScope = ng.IScope;
type Callback = () => void;

Type aliases are exactly the same as their original types; they are simply alternative names. Type aliases can help us to make our code more readable but it can also lead to some problems.

If you work as part of a large team, the indiscriminate creation of aliases can lead to maintainability problems. In the book, Maintainable JavaScript, Nicholas C. Zakas, the author recommends to avoid modifying objects you don't own. Nicholas was talking about adding, removing, or overriding methods in objects that have not been declared by you (DOM objects, BOM objects, primitive types, and third-party libraries) but we can apply this rule to the usage of aliases as well.

Ambient declarations

Ambient declaration allows you to create a variable in your TypeScript code that will not be translated into JavaScript at compilation time. This feature was designed to facilitate integration with the existing JavaScript code, the DOM (Document Object Model), and BOM (Browser Object Model). Let's take a look at an example:

customConsole.log("A log entry!");  // error

If you try to call the member log of an object named customConsole, TypeScript will let us know that the customConsole object has not been declared:

// Cannot find name 'customConsole'

This is not a surprise. However, sometimes we want to invoke an object that has not been defined, for example, the console or window objects.

console.log("Log Entry!");
var host = window.location.hostname;

When we access DOM or BOM objects, we don't get an error because these objects have already been declared in a special TypeScript file known as declaration files. You can use the declare operator to create an ambient declaration.

In the following code snippet, we will declare an interface that is implemented by the customConsole object. We then use the declare operator to add the customConsole object to the scope:

interface ICustomConsole {
    log(arg : string) : void;
}
declare var customConsole : ICustomConsole;

Note

Interfaces are explained in greater detail later in the chapter.

We can then use the customConsole object without compilation errors:

customConsole.log("A log entry!"); // ok

TypeScript includes, by default, a file named lib.d.ts that provides interface declarations for the built-in JavaScript library as well as the DOM.

Declaration files use the file extension .d.ts and are used to increase the TypeScript compatibility with third-party libraries and run-time environments such as Node.js or a web browser.

Note

We will learn how to work with declaration files in Chapter 2, Automating Your Development Workflow.

Arithmetic operators

There following arithmetic operators are supported by the TypeScript programming language. In order to understand the examples, you must assume that variable A holds 10 and variable B holds 20.

Operator

Description

Example

+

This adds two operands

A + B will give 30

-

This subtracts the second operand from the first

A - B will give -10

*

This multiplies both the operands

A * B will give 200

/

This divides the numerator by the denominator

B / A will give 2

%

This is the modulus operator and remainder after an integer division

B % A will give 0

++

This is the increment operator that increases the integer value by 1

A++ will give 11

--

This is the decrement operator that decreases the integer value by 1

A-- will give 9

Comparison operators

The following comparison operators are supported by the TypeScript language. In order to understand the examples, you must assume that variable A holds 10 and variable B holds 20.

Operator

Description

Example

==

This checks whether the values of two operands are equal or not. If yes, then the condition becomes true.

(A == B) is false. A == "10" is true.

===

This checks whether the value and type of two operands are equal or not. If yes, then the condition becomes true.

A === B is false. A === "10" is false.

!=

This checks whether the values of two operands are equal or not. If the values are not equal, then the condition becomes true.

(A != B) is true.

>

This checks whether the value of the left operand is greater than the value of the right operand; if yes, then the condition becomes true.

(A > B) is false.

<

This checks whether the value of the left operand is less than the value of the right operand; if yes, then the condition becomes true.

(A < B) is true.

>=

This checks whether the value of the left operand is greater than or equal to the value of the right operand; if yes, then the condition becomes true.

(A >= B) is false.

<=

This checks whether the value of the left operand is less than or equal to the value of the right operand; if yes, then the condition becomes true.

(A <= B) is true.

Logical operators

The following logical operators are supported by the TypeScript language. In order to understand the examples, you must assume that variable A holds 10 and variable B holds 20.

Operator

Description

Example

&&

This is called the logical AND operator. If both the operands are nonzero, then the condition becomes true.

(A && B) is true.

||

This is called logical OR operator. If any of the two operands are nonzero, then the condition becomes true.

(A || B) is true.

!

This is called the logical NOT operator. It is used to reverse the logical state of its operand. If a condition is true, then the logical NOT operator will make it false.

!(A && B) is false.

Bitwise operators

The following bitwise operators are supported by the TypeScript language. In order to understand the examples, you must assume that variable A holds 2 and variable B holds 3.

Operator

Description

Example

&

This is called the Bitwise AND operator. It performs a Boolean AND operation on each bit of its integer arguments.

(A & B) is 2

|

This is called the Bitwise OR operator. It performs a Boolean OR operation on each bit of its integer arguments.

(A | B) is 3.

^

This is called the Bitwise XOR operator. It performs a Boolean exclusive OR operation on each bit of its integer arguments. Exclusive OR means that either operand one is true or operand two is true, but not both.

(A ^ B) is 1.

~

This is called the Bitwise NOT operator. It is a unary operator and operates by reversing all bits in the operand.

(~B) is -4

<<

This is called the Bitwise Shift Left operator. It moves all bits in its first operand to the left by the number of places specified in the second operand. New bits are filled with zeros. Shifting a value left by one position is equivalent to multiplying by 2, shifting two positions is equivalent to multiplying by 4, and so on.

(A << 1) is 4

>>

This is called the Bitwise Shift Right with sign operator. It moves all bits in its first operand to the right by the number of places specified in the second operand.

(A >> 1) is 1

>>>

This is called the Bitwise Shift Right with zero operators. This operator is just like the >> operator, except that the bits shifted in on the left are always zero,

(A >>> 1) is 1

Note

One of the main reasons to use bitwise operators in languages such as C++, Java, or C# is that they're extremely fast. However, bitwise operators are often considered not that efficient in TypeScript and JavaScript. Bitwise operators are less efficient in JavaScript because it is necessary to cast from floating point representation (how JavaScript stores all of its numbers) to a 32-bit integer to perform the bit manipulation and back.

Assignment operators

The following assignment operators are supported by the TypeScript language.

Operator

Description

Example

=

This is a simple assignment operator that assigns values from the right-side operands to the left-side operand.

C = A + B will assign the value of A + B into C

+=

This adds the AND assignment operator. It adds the right operand to the left operand and assigns the result to the left operand.

C += A is equivalent to C = C + A

-=

This subtracts the AND assignment operator. It subtracts the right operand from the left operand and assigns the result to the left operand.

C -= A is equivalent to C = C - A

*=

This multiplies the AND assignment operator. It multiplies the right operand with the left operand and assigns the result to the left operand.

C *= A is equivalent to C = C * A

/=

This divides the AND assignment operator. It divides the left operand with the right operand and assigns the result to the left operand.

C /= A is equivalent to C = C / A

%=

This is the modulus AND assignment operator. It takes the modulus using two operands and assigns the result to the left operand.

C %= A is equivalent to C = C % A

Flow control statements

This section describes the decision-making statements, the looping statements, and the branching statements supported by the TypeScript programming language.

The single-selection structure (if)

The following code snippet declares a variable of type Boolean and name isValid. Then, an if statement will check whether the value of isValid is equal to true. If the statement turns out to be true, the Is valid! message will be displayed on the screen.

var isValid : boolean = true;

if(isValid) {
  alert("is valid!");
}

The double-selection structure (if…else)

The following code snippet declares a variable of type Boolean and name isValid. Then, an if statement will check whether the value of isValid is equal to true. If the statement turns out to be true, the message Is valid! will be displayed on the screen. On the other side, if the statement turns out to be false, the message Is NOT valid! will be displayed on the screen.

var isValid : boolean = true;

if(isValid) {
  alert("Is valid!");
}
else {
  alert("Is NOT valid!");
}

The inline ternary operator (?)

The inline ternary operator is just an alternative way of declaring a double-selection structure.

var isValid : boolean = true;
var message = isValid ? "Is valid!" : "Is NOT valid!";
alert(message);

The preceding code snippet declares a variable of type Boolean and name isValid. Then it checks whether the variable or expression on the left-hand side of the operator ? is equal to true.

If the statement turns out to be true, the expression on the left-hand side of the character will be executed and the message Is valid! will be assigned to the message variable.

On the other hand, if the statement turns out to be false, the expression on the right-hand side of the operator will be executed and the message, Is NOT valid! will be assigned to the message variable.

Finally, the value of the message variable is displayed on the screen.

The multiple-selection structure (switch)

The switch statement evaluates an expression, matches the expression's value to a case clause, and executes statements associated with that case. A switch statement and enumerations are often used together to improve the readability of the code.

In the following example, we will declare a function that takes an enumeration AlertLevel. Inside the function, we will generate an array of strings to store e-mail addresses and execute a switch structure. Each of the options of the enumeration is a case in the switch structure:

enum AlertLevel{
  info,
  warning,
  error
}

function getAlertSubscribers(level : AlertLevel){
  var emails = new Array<string>();
  switch(level){
    case AlertLevel.info:
       emails.push("[email protected]");
       break;
    case AlertLevel.warning:
       emails.push("[email protected]");
       emails.push("[email protected]");
       break;
    case AlertLevel.error:
      emails.push("[email protected]");
      emails.push("[email protected]");
      emails.push("[email protected]");
      break;
    default:
      throw new Error("Invalid argument!");
  }
  return emails;
}

getAlertSubscribers(AlertLevel.info); // ["[email protected]"]
getAlertSubscribers(AlertLevel.warning); // ["[email protected]", "[email protected]"]

The value of the level variable is tested against all the cases in the switch. If the variable matches one of the cases, the statement associated with that case is executed. Once the case statement has been executed, the variable is tested against the next case.

Once the execution of the statement associated to a matching case is finalized, the next case will be evaluated. If the break keyword is present, the program will not continue the execution of the following case statement.

If no matching case clause is found, the program looks for the optional default clause, and if found, it transfers control to that clause and executes the associated statements.

If no default clause is found, the program continues execution at the statement following the end of switch. By convention, the default clause is the last clause, but it does not have to be so.

The expression is tested at the top of the loop (while)

The while expression is used to repeat an operation while a certain requirement is satisfied. For example, the following code snippet, declares a numeric variable i. If the requirement (the value of i is less than 5) is satisfied, an operation takes place (increase the value of i by 1 and display its value in the browser console). Once the operation has completed, the accomplishment of the requirement will be checked again.

var i : number = 0;
while (i < 5) {
  i += 1;
  console.log(i);
}

In a while expression, the operation will take place only if the requirement is satisfied.

The expression is tested at the bottom of the loop (do…while)

The do-while expression is used to repeat an operation until a certain requirement is not satisfied. For example, the following code snippet declares a numeric variable i and repeats an operation (increase the value of i by 1 and display its value in the browser console) for as long as the requirement (the value of i is less than 5) is satisfied.

var i : number = 0;
do {
  i += 1;
  console.log(i);
} while (i < 5);

Unlike the while loop, the do-while expression will execute at least once regardless of the requirement value as the operation will take place before checking if a certain requirement is satisfied or not.

Iterate on each object's properties (for…in)

The for-in statement by itself is not a bad practice; however, it can be misused, for example, to iterate over arrays or array-like objects. The purpose of the for-in statement is to enumerate over object properties.

var obj : any = { a:1, b:2, c:3 };    
for (var key in obj) {
  console.log(key + " = " + obj[key]);
}

// Output:
// "a = 1"
// "b = 2"
// "c = 3"

The following code snippet will go up in the prototype chain, also enumerating the inherited properties. The for-in statement iterates the entire prototype chain, also enumerating the inherited properties. When you want to enumerate only the object's own properties (the ones that aren't inherited), you can use the hasOwnProperty method:

for (var key in obj) {
  if (obj.hasOwnProperty(prop)) { 
    // prop is not inherited 
  }
}

Counter controlled repetition (for)

The for statement creates a loop that consists of three optional expressions, enclosed in parentheses and separated by semicolons, followed by a statement or a set of statements executed in the loop.

for (var i: number = 0; i < 9; i++) {
   console.log(i);
}

The preceding code snippet contains a for statement, it starts by declaring the variable i and initializing it to 0. It checks whether i is less than 9, performs the two succeeding statements, and increments i by 1 after each pass through the loop.

Functions

Just as in JavaScript, TypeScript functions can be created either as a named function or as an anonymous function. This allows us to choose the most appropriate approach for an application, whether we are building a list of functions in an API or a one-off function to hand over to another function.

// named function
function greet(name? : string) : string {
  if(name){
    return "Hi! " + name;
  }
  else
  {
    return "Hi!";
  }
}

// anonymous function
var greet = function(name? : string) : string {
  if(name){
    return "Hi! " + name;
  }
  else
  {
    return "Hi!";
  }
}

As we can see in the preceding code snippet, in TypeScript we can add types to each of the parameters and then to the function itself to add a return type. TypeScript can infer the return type by looking at the return statements, so we can also optionally leave this off in many cases.

There is an alternative function syntax, which uses the arrow (=>) operator after the function’s return type and skips the usage of the function keyword.

var greet = (name : string) : string =>  {
    if(name){
      return "Hi! " + name;
    }
    else
    {
      return "Hi! my name is " + this.fullname;
    }
};

The functions declared using this syntax are commonly known as arrow functions. Let's return to the previous example in which we were assigning an anonymous function to the greet variable. We can now add the type annotations to the greet variable to match the anonymous function signature.

var greet : (name : string) => string = function(name : string) : string {
    if(name){
      return "Hi! " + name;
    }
    else
    {
      return "Hi!";
    }
};

Note

Keep in mind that the arrow function (=>) syntax changes the way the this operator works when working with classes. We will learn more about this in the upcoming chapters.

Now you know how to add type annotations to force a variable to be a function with a specific signature. The usage of this kind of annotations is really common when we use a call back (functions used as an argument of another function).

function sume(a : number, b : number, callback : (result:number) => void){
  callback(a+b);
}

In the preceding example, we are declaring a function named sume that takes two numbers and a callback as a function. The type annotations will force the callback to return void and take a number as its only argument.

Note

We will focus on functions in Chapter 3, Working with Functions.

Classes

ECMAScript 6, the next version of JavaScript, adds class-based object orientation to JavaScript and, since TypeScript is based on ES6, developers are allowed to use class-based object orientation today, and compile them down to JavaScript that works across all major browsers and platforms, without having to wait for the next version of JavaScript.

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

class Character {
  fullname : string;
  constructor(firstname : string, lastname : string) {
    this.fullname = firstname + " " + lastname;
  }
  greet(name? : string) {
    if(name)
    {
      return "Hi! " + name + "! my name is " + this.fullname;
    }
    else
    {
      return "Hi! my name is " + this.fullname;
    }
  }
}

var spark = new Character("Jacob","Keyes");
var msg = spark.greet();             
alert(msg); // "Hi! my name is Jacob Keyes"
var msg1 = spark.greet("Dr. Halsey"); 
alert(msg1); // "Hi! Dr. Halsey! my name is Jacob Keyes"

In the preceding example, we have declared a new class Character. This class has three members: a property called fullname, a constructor, and a method greet. When we declare a class in TypeScript, all the methods and properties are public by default.

You'll notice that when we refer to one of the members of the class (from within itself) we prepend the this operator. The this operator denotes that it's a member access. In the last lines, we construct an instance of the Character class using a new operator. This calls into the constructor we defined earlier, creating a new object with the Character shape, and running the constructor to initialize it.

TypeScript classes are compiled into JavaScript functions in order to achieve compatibility with ECMAScript 3 and ECMAScript 5.

Note

We will learn more about classes and other object-oriented programming concepts in Chapter 4, Object-Oriented Programming with TypeScript.

Interfaces

In TypeScript, we can use interfaces to enforce that a class follow the specification in a particular contract.

interface LoggerInterface{
  log(arg : any) : void;
}

class Logger implements LoggerInterface{
  log(arg){
    if(typeof console.log === "function"){
      console.log(arg);
    }
    else
    {
      alert(arg);
    }
  }
}

In the preceding example, we have defined an interface loggerInterface and a class Logger, which implements it. TypeScript will also allow you to use interfaces to declare the type of an object. This can help us to prevent many potential issues, especially when working with object literals:

interface UserInterface{
  name : string;
  password : string;
}

var user : UserInterface = {
  name : "",
  password : "" // error property password is missing
};

Note

We will learn more about interfaces and other object-oriented programming concepts in Chapter 4, Object-Oriented Programming with TypeScript.

Namespaces

Namespaces, also known as internal modules, are used to encapsulate features and objects that share a certain relationship. Namespaces will help you to organize your code in a much clearer way. To declare a namespace in TypeScript, you will use the namespace and export keywords.

namespace Geometry{
    interface VectorInterface {
        /* ... */
    }
  export interface Vector2dInterface {
        /* ... */
    }
  export interface Vector3dInterface {
        /* ... */
    }
  export class Vector2d implements VectorInterface, Vector2dInterface {
    /* ... */
  }
  export class Vector3d implements VectorInterface, Vector3dInterface {
    /* ... */
  }
}

var vector2dInstance : Geometry.Vector2dInterface = new Geometry.Vector2d();
var vector3dInstance : Geometry.Vector3dInterface = new Geometry.Vector3d();

In the preceding code snippet, we have declared a namespace that contains the classes vector2d and vector3d and the interfaces VectorInterface, Vector2dInterface, and Vector3dInterface. Note that the first interface is missing the keyword export. As a result, the interface VectorInterface will not be accessible from outside the namespace's scope.

Note

In Chapter 4, Object-Oriented Programming with TypeScript, we'll be covering namespaces (internal modules) and external modules and we'll discuss when each is appropriate and how to use them.

 

Putting everything together


Now that we have learned how to use the basic TypeScript building blocks individually, let's take a look at a final example in which we will use modules, classes, functions, and type annotations for each of these elements:

module Geometry{
  export interface Vector2dInterface {
    toArray(callback : (x : number[]) => void) : void;
    length() : number;
    normalize();
  }
  export class Vector2d implements Vector2dInterface {
    private _x: number;
    private _y : number;
    constructor(x : number, y : number){
      this._x = x;
      this._y = y;
    }
    toArray(callback : (x : number[]) => void) : void{
      callback([this._x, this._y]);
    }
    length() : number{
      return Math.sqrt(this._x * this._x + this._y * this._y);
    }
    normalize(){
      var len = 1 / this.length();
      this._x *= len;
      this._y *= len;
    }
  }
}

The preceding example is just a small portion of a basic 3D engine written in JavaScript. In 3D engines, there are a lot of mathematical calculations involving matrices and vectors. As you can see, we have defined a module Geometry that will contain some entities; to keep the example simple, we have only added the class Vector2d. This class stores two coordinates (x and y) in 2d space and performs some operations on the coordinates. One of the most used operations on vectors is normalization, which is one of the methods in our Vector2d class.

3D engines are complex software solutions, and as a developer, you are much more likely to use a third-party 3D engine than create your own. For this reason, it is important to understand that TypeScript will not only help you to develop large-scale applications, but also to work with large-scale applications. In the following code snippet, we will use the module declared earlier to create a Vector2d instance:

var vector : Geometry.Vector2dInterface = new Geometry.Vector2d(2,3);
vector.normalize();
vector.toArray(function(vectorAsArray : number[]){
  alert(' x :' + vectorAsArray[0] + ' y : '+ vectorAsArray[1]);
});

The type checking and IntelliSense features will help us create a Vector2d instance, normalize its value, and convert it into an array to finally show its value on screen with ease.

 

Summary


In this chapter, you have learned about the purposes of TypeScript. You have also learned about some of the design decisions made by the TypeScript engineers at Microsoft.

Towards the end of this chapter, you learned a lot about the basic building blocks of a TypeScript application .You started to write some TypeScript code for the first time and you can now work with type annotations, variables and primitive data types, operators, flow control statements, functions, and classes.

In the next chapter, you will learn how to automate your development workflow.

About the Author

  • Remo H. Jansen

    Remo H. Jansen lives in Dublin, Ireland, where he works as the managing director of Wolk Software Limited and as a part-time lecturer at CCT College Dublin. Remo is a Microsoft MVP and an active member of the TypeScript community. He is the author of Learning TypeScript 2.x, organizes the Dublin TypeScript and Dublin OSS meetups, writes a blog, and maintains some open source projects on GitHub. Remo is available for conference talks, independent consulting, and corporate training services opportunities.

    Browse publications by this author

Latest Reviews

(4 reviews total)
Good
Good intro to TypeScript that will be a good background for continuing on with version 2, which will be released in the near future.
Good