Over a million developers have joined DZone.
{{announcement.body}}
{{announcement.title}}

TypeScript Practical Introduction, Part 1

DZone's Guide to

TypeScript Practical Introduction, Part 1

With the rising popularity of Angular, TypeScript is becoming more and more important to know. Check out this great introduction!

· Web Dev Zone ·
Free Resource

Learn how Crafter’s Git-based content management system is reinventing modern digital experiences.

In this article, we are going to learn the basic features of TypeScript through practical examples. We'll start with a small introduction to TypeScript, then we are going to create a TypeScript project and use its features to learn the language in practice.

What Is TypeScript?

TypeScript is a programming language, built by Microsoft, that extends JavaScript. The language has been built as an open source project, licensed under the Apache License 2.0, so the developer community can use it freely. Among its features, a remarkable one is that TypeScript brings type safety to one of the most flexible, dynamic programming languages around, JavaScript. This characteristic enables developers to be very productive even on large codebases by introducing and facilitating tools and practices like static checking and code refactoring.

Besides enabling developers to check the correctness of their code before running it, TypeScript also brings to the table the state of the art features of JavaScript. That is, with TypeScript we can take advantage of the latest features of JavaScript, like those introduced by ECMAScript 2015, and features that are still under consideration (e.g. decorators). As the developers of TypeScript knew that applications written in the language would face a wide variety of environments (i.e. JavaScript engines), they made it easy to compile and transpile TypeScript to different targets, like ECMAScript 3 and above.

Installing TypeScript

To compile and transpile TypeScript into JavaScript, we need to install the command-line compiler. As this compiler is, in fact, a Node.js program, we first need to install Node.js and NPM on our environment. After that, we can install the compiler as a Node.js package:

# installing TypeScript compiler
npm install -g typescript

This will make the tsc (TypeScript Compiler) command available globally on our machine. To test the installation, let's create a simple TypeScript file called index.ts with the following code:

console.log(1);

Then let's use the compiler to transform it to JavaScript:

# compiling index.ts
tsc index

This will generate a new file called index.js with the exact same code of the TypeScript file. To execute this new file, let's use Node.js:

# this will output 1
node index

Although the compiler did nothing else besides create a JavaScript file and copy the original code to it, these steps helped us to validate that our TypeScript installation is in good shape and ready to handle the next steps.

Creating a TypeScript Project

To create a TypeScript project, everything that we need is in a tsconfig.json file. The presence of this file in a directory denotes that this directory is the root of a TypeScript project. This file, among other things, instructs the compiler on which files to compile, which files to ignore, and what environment to target (e.g. ECMAScript 3).

To create our first TypeScript project, let's create a new directory and add the TypeScript configuration file to it:

# create the root directory of our TypeScript project
mkdir project-manager-ts

# move the working directory to it
cd project-manager-ts

# create an empty tsconfig.json file
touch tsconfig.json

TypeScript Configuration

As TypeScript can aim different environments (like ECMAScript 3 or ECMAScript 2015) and can be configured to validate different things (like implicit any), we use the tsconfig.json file to adjust the compiler to our needs. In our practical introduction to TypeScript, we will create a small program that deals with tasks and stories and that runs on our terminal, through Node.js, to help us understand the concepts.

Node.js is based on Chrome V8, one of the most up-to-date JavaScript engines available. Version 6 of Node.js ships with a Chrome V8 version capable of supporting 95% of the ECMAScript 2015 specification, as shown by Node Green, while version 8 is capable of supporting 99%. In respect to ECMAScript 2017, both versions support 23% and 73% of the specification, respectively. Therefore, the best choice is to configure our project to be compiled to ECMAScript 2015, which will enable users with Node.js 6 and 8 to run our program without trouble.

Besides configuring the compiler to target ECMAScript 2015, we will also configure four other characteristics:

  • module, to instruct TypeScript to use the CommonJS module format.
  • removeComments, to avoid adding comments in the generated JavaScript files.
  • sourceMap, to help us in debugging the code generated.
  • outDir, to define where the compiled code will reside.

We will also tell the compiler to process files under ./src, a directory that we will create to which we will add our TypeScript source code. To perform these configurations, let's open the tsconfig.json file and add the following content to it:

{
  "compilerOptions": {
    "module": "commonjs",
    "target": "es2015",
    "removeComments": true,
    "outDir": "./bin"
  },
  "include": ["src/**/*"]
}

Compiler Options

The options used in the configuration file above are just a small subset of what TypeScript supports. For example, we could instruct the compiler to handle decorators, to support jsx files, or even to transpile pure JavaScript files. The official website contains a list of all the options available on TypeScript, but below is an explanation of a few commonly used options:

  • allowJs: this option makes TypeScript compiler process JavaScript as well. For example, if we create a JavaScript file that uses generator (ECMAScript 2015 feature), and use TypeScript to compile JavaScript targeting ECMAScript 5, the compiler would replace the generator function with JavaScript code capable of running on the target.
  • noImplicitAny: this options makes the compiler complain whenever it finds a variable declaration that can accept any type, but that doesn't explicitly define it.
  • experimentalDecorators: this option enables decorators on a TypeScript project. By default, decorators are disabled since they are not part of any official JavaScript version yet.
  • emitDecoratorMetadata: this option, alongside with the presence of the reflect-metadata package, preserves type information in an object's metadata.
  • watch: this option makes the TypeScript compiler run indefinitely. With it, whenever a source file is changed, the compiling process is triggered automatically to generate the new version.

TypeScript Features

Now that we understand how to bootstrap a TypeScript project and configure the compiler, let's start learning about the features provided by TypeScript. Although the features that we are going to cover here (and a few more) are thoroughly explained in the TypeScript handbook, this article will focus on a more practical approach. We will learn the basic concepts of the most important features and put them to work together.

TypeScript Types, Variables, and Functions

Before creating anything that matters, we need to learn about the most important features of TypeScript: types, variables, and functions. TypeScript became popular over the last couple of years because it enables developers to define strongly typed variables, parameters, and return values while still using JavaScript (which by its nature is weakly typed and extremely flexible). For example, developing in JavaScript, the following code is perfectly valid:

function addOne(age) {
    return age + 1;
}

let age = 32;
// ...
age = "thirty two";

console.log(addOne(age));

Running it on Node.js, or on any browser for that matter, would output thirty two1 without generating any warning. Nothing new here, it's just JavaScript behaving as flexible as always. But, what if we want to guarantee that the addOne function accepts only numbers when called? We could change the code to validate the parameter during runtime... or we could use TypeScript to restrict that during compile time:

// note that we restricted age to accept numbers only
function addOne(age: number): number {
    return age + 1;
}

console.log(addOne(32));
console.log(addOne("thirty two"));

Now, using the TypeScript compiler to generate JavaScript will produce an error saying:

Argument of type '"thirty two"' is not assignable to parameter of type 'number'.

This is the most valuable feature of TypeScript. Based on this feature amazing tools and IDE integrations can be created to improve tasks like code refactoring. Being able to define types during design time helps us avoid mistakes like passing the wrong variable type to functions.

String and number are two of the basic types that TypeScript supports. Besides these types, TypeScript also supports:

  • Boolean: flag that contains true or false values.
  • Array: collection of typed elements.
  • Tuple: similar to array, but with a fixed number of typed elements.
  • Enum: friendly names to sets of numeric values.
  • Any: an indicator that a variable/parameter can be anything at all.
  • Void: to indicate that a function won't return anything.
  • Never: to indicate that a function always throws an exception or never finishes its execution.

These types enable us to create solutions to whatever problem we face. With them, we can develop code that guarantees that the correct types are being passed around and, if we need to represent more complex entities, we can define classes that contain any arbitrary number of typed variables (as we will see in the next section). To provide an overview of what TypeScript makes possible, let's take a look at the following code:

// 1 - declaring a type
type RankingTuple = [number, string, boolean];

// 2 - defining typed variables
let position: number;
let playerName: string;
let finishedGame: boolean;
let ranking: RankingTuple;
let hallOfFame: Array<RankingTuple> = [];

// 3 - creating a ranking
position = 1;
playerName = "Bruno Krebs";
finishedGame = true;
ranking = [position, playerName, finishedGame];
hallOfFame.push(ranking);

// 4 - creating another ranking
position = 2;
playerName = "Maria Helena";
finishedGame = true;
ranking = [position, playerName, finishedGame];
hallOfFame.push(ranking);

// 5 - defining a function that logs all rankings
function printRankings(rankings: Array<RankingTuple>): void {
  for (let ranking of rankings) {
    console.log(ranking);
  }
}

// 6 - calling the function
printRankings(hallOfFame);

The first step executed by the code above creates a type (tuple) that accepts three objects: a number, a string, and a boolean. This tuple represents a ranking where the number is the position, the string is the name of the person in that position, and the boolean indicates whether the player has finished the game or not. In the second step, we define five typed variables to hold:

  1. A player's position.
  2. A player's name.
  3. A flag that indicates if the player has finished the game or not.
  4. A reference to an object that complies to the ranking tuple.
  5. And an array of rankings.

Steps three and four use these variables to create two rankings and to push them to the array of rankings ( hallOfFame). Step five defines a function that iterates over an array of rankings and prints them. The last step simply calls the function defined in the previous step passing the array of rankings ( hallOfFame). Note that all these steps were defined in such a way that the compiler is able to validate everything before runtime. For example, trying to push something else other than a tuple that contains exactly a number, a string, and a boolean to hallOfFame would generate an error. Another example is that trying to capture the return value of a call to printRankings would also generate an error since this function explicitly defines that it returns nothing (: void).

To learn more about the basic types of TypeScript, take a look at the documentation. The official website also contains a section that talks about advanced types, like the type that we declared in the first step of the example above.

TypeScript Modules

Another feature that TypeScript inherits from ECMAScript 2015 is the support for modules. Modules in both languages (JavaScript and TypeScript) are a way to encapsulate code into their own scope. That is, when we create a module and add some code to it, we are not letting anything (like variables, functions, etc.) escape silently. Everything that we want to provide externally needs to be explicitly exported by our module and explicitly imported by other modules/scripts.

For example, if we change the code in the previous section by adding the following line:

// steps 1 to 6 ...

export { printRankings, RankingTuple };

We are then able to import the printRankings function and the RankingTuple type definition on other modules/files to use them. Throughout this article, we are going to define and export multiple classes and import them into other files. As each of these classes will be defined in their own files, they can be considered separate modules. To learn more about modules, check out the official documentation.

Tune in next time when we'll discuss TypeScript classes, interfaces, decorators, and iterators!

Crafter CMS is a modern Git-based platform for building innovative websites and content-rich digital experiences. Download this white paper now.

Topics:
web dev ,typescript ,web application development

Published at DZone with permission of

Opinions expressed by DZone contributors are their own.

{{ parent.title || parent.header.title}}

{{ parent.tldr }}

{{ parent.urlSource.name }}