TypeScript Tutorial For Beginners

JavaScript has expanded its application domain far beyond the browser. It is heavily used in the backend with Node.js as a framework, and the creation of many cross-platform mobile and desktop applications with frameworks like NativeScript, Ionic and Electron are now made possible. Due to such a boom, demand for high performance and maintainable JavaScript occurred and has led to the introduction of TypeScript. Let’s explore in this tutorial what TypeScript is, why it is used, benefits that TypeScript brings to any codebase.

What is TypeScript?

TypeScript is an open-source programming language designed by Microsoft for large-scale JavaScript application development, it has been designed as a typed superset of JavaScript. Even, the official website says: "a typed superset of JavaScript that compiles to plain javascript" but it assumes that you know what a "superset" is and what "typed" means. Let’s break the statement to understand more.

Firstly, Typescript is typed which means that unlike Javascript, Typescript considers the types of variables, parameters, and functions just like in any other statically typed language in which the compiler checks our code at compile-time for type errors.

Secondly, Typescript is a superset of Javascript which means, the Typescript language is built on top of Javascript that takes everything present in Javascript and adds new features and advantages to it.

Third and the final point is - Typescript compiles into plain Javascript which means that you write Typescript code with all the new features and advantages and you get normal Javascript code after compiling. The reason for this conversion is because browsers do not understand Typescript’s added syntax, therefore it is compiled by the Typescript compiler into plain Javascript which can be understood by the browser.

Now you might be thinking how can typescript add new features if what you get in the end is regular Javascript? Actually, the typescript compiler compiles these new features to javascript “workarounds” which means it gives you a nicer syntax and easy way of writing code and it then compiles to a more complex Javascript snippet which you would have to write otherwise. This means that it can't add what’s not possible in the JavaScript language.

Now you may have a better clarity on what Typescript is. But, a big question arises i.e why Typescript makes sense, after all, it gets stripped down to Javascript code.

Why TypeScript?

In order to understand why Typescript is created, let us understand the problem with Javascript.

Look at this simple Javascript code:

function add(num1, num2) {
   return num1 + num2;
}

console.log(add('2', '3')); // output will be '23' rather than 5

Since we are calling the add() with strings as an argument, the result would be unwanted behavior i.e. string. This is not a technical error but a logical error that arises due to the absence of type checking and can lead to huge problems in web applications you are writing with Javascript.

Another problem was with Javascript objects in which you cannot enforce the structure of objects.

Let's say we have a person object:

const person = {
    firstname : "After",
    lastname : "Academy"
}

// prints 'person' object
console.log(person); 

Now, what if I want to make sure that nobody adds any other properties to it.

Well, there is nothing you can do to prevent somebody from doing it.

// valid syntax
person.foo = 10;

// prints new 'person' with foo property
console.log(person); 

This happens because unlike other languages, objects in Javascript are loosely coupled. They don't have a proper template.

Even after these problems, we are heavily dependent on Javascript as browsers support only this language. Therefore as a solution to the above problems, Typescript was introduced. Typescript solves the problems and then compiles into Javascript so that browsers can execute the new error-free code. Remember that Typescript compiles into Javascript which means we are now able to catch errors(with Typescript) during development time(compile-time), unlike Javascript that throws such errors at runtime.

Setting Up TypeScript

To install Typescript, make sure you have node installed. Create a Node Project folder and run the following command:

npm install typescript --save-dev

To check the version you are using, you can run the following command:

tsc -v

Here, tsc stands for TypeScript compiler. Whenever compiler runs it will look for a file with a name tsconfig.json in the project folder.

tsconfig.json is more or less similar to package.json. As package.json is used to give npm instructions on what packages to install as dependencies, we can use a tsconfig.json file to provide instructions on how our Typescript project should be configured. Adding this file to our directory, marks it as the root directory of our Typescript project. In this file, we can also specify compiler options to compile our .ts files as well as root files for our project.

Generate a tsconfig.json file within our directory with the following command:

tsc --init

If everything goes well, a "message TS6071: Successfully created a tsconfig.json file" will be logged on to the console.

Configure this file to tell the compiler to use ES5 as the Javascript compilation target instead. So, replace all the contents with this code :

{ 
   "compilerOptions" : { 
       "target": "es5",
       "strict" : true
    } 
}

here, we have set "strict" property to true, by this, we have ensured that Typescript will now be more strict and generates various type errors. You can also enable all strict type-checking options as below:

  • noImplicitAny true: if this is enabled, Typescript will report any error whenever it has inferred any.
  • alwaysStrict true: Typescript will always parse in strict mode and emit "use strict" for each source file.

Now, In the directory instead of creating a Javascript file, we will create a Typescript file with the name app.ts. So run the following command:

touch app.ts

In this file, we can start writing our Typescript code. Typescript can be written as a regular Javascript also. Test it by writing a simple Typescript code in app.ts:

var message: string = "Hello, AfterAcademy";
console.log(message);

Now compile the file by writing the following command:

tsc app.ts

When we run the above command, the Typescript compiler first looks for any issues in the code. If there are none, the compiler then compiles our code down to a specified version of Javascript, such as ES5. Notice that a file app.js gets created automatically. This file contains the compiled Javascript which in turn gets executed by the Javascript engine.

Now run this newly generated file by the following command and you will see "Hello, AfterAcademy" is logged on the console.

node app.js

Variables

Variables are the fields that are defined inside a class or a function. TypeScript allows us to define a variable using the keyword "var", "let" and "const" and assign a data type to it. Once a data type is assigned, any further usage of the variable has to be with the same data type, else TypeScript will generate errors on design and compile time. TypeScript also infers the type of a variable and then treats it as that type when a variable is declared and initialized. This is called type inference. Whenever TypeScript is not able to infer the type, it will assign "any" as the type to the variable.

Now, Let's take a look at some of the types provided by Typescript.

Types in TypeScript

TypeScript provides many data types to Javascript. Javascript also has some data types but Typescript adds more to it and also allows users to define their own types. In Typescript, a colon ":" is used to assign a type for a variable followed by the name of the type. Let’s look at an example.

let variableName: typeScriptType = value;

So, let's start with some core types which Typescript supports.

1. number: Typescript supports decimal, hexadecimal, octal, and binary literal. In Typescript, all numbers are floating-point values, no distinction between integers and floats.

let integer: number = 5;    // integer
let real: number = 5.5;    // float
let hexa: number = 0xde42; // hexadecimal
let bin: number = 0b0010; // binary

2. string: Typescript follows the same syntax of Javascript with double or single quotes around the text. You can also use the backticks. Like javascript, here also we cannot define individual character. Typescript will consider them as string only.

let a: string = "Hello, AfterAcademy";
let b: string = "String in double quotes -  " + a;
let c: string = 'String in single quotes - ' + a;
let d: string = `String in template literal - ${a}`;

console.log(a,b,c,d);

3. boolean: Boolean values function just like they do in Javascript.

let yes: boolean = true; 
let no: boolean = false;

4. object: Unlike Javascript, Typescript supports more specific types of objects. We can make an object containing only specific properties as well.

// 'person' object is fixed now, you cannot 
// assign any new properties to it, unlike JavaScript
let person = {
   name: 'John Doe',
   age: 30
};

console.log(person.name);

Here, if you try to assign "age" with any other type than "number", or try to assign some other property to it, Typescript will throw an error.

For example, if we try to add a new property called 'nickname'.

person.nickname = 'Johnny';

Typescript will throw this error:

This is because Typescript reads 'person' object like this:

let person: {
   name: string;
   age: number;
} = {
   name: 'John Doe',
   age: 30
};

This shows that we have already told Typescript to create an object that contains only, name and age as property. This solves the problem that we faced in Javascript.

5. Array: In Typescript, by default arrays are a collection of the same object, unlike Javascript. You can declare a typed array in two ways, either with the data type followed by [ ], or the generic array approach with Array<elementType>.

// declaring array
let str: string [] = ['AfterAcademy'];

// OR
let str: Array<string> = ['AfterAcademy'];

console.log(str); // logs ['AfterAcademy'] to console

If you want to make an array that can store multiple values, use ":" and then you have to specify all the types that will get stored in the array.

For example:

// declaring array with multiple type elements
let str: (string | number)[] = ['AfterAcademy', 1, 2.2];

console.log(str);

6. Tuple: Tuple is similar to an array and is used to make organized arrays. We can define the type of data that is stored in each position. They are also called "Fixed length, Fixed type array".

// declaring tuple
let tupleExample: [number, string] = [1, 'AfterAcademy'];

console.log(tupleExample);

Here, even if you interchange the position of elements the compiler will throw an error, unlike in arrays.

7. enum: Enums allow us to define a set of predefined constants. These are defined with the enum keyword. You can define a string or a numeric enum. Generally, the values are declared in capital letters, but that is not a "must-do".

// declaring enum
enum Role { ADMIN = 'ADMIN', READER = 'READER', AUTHOR = 'AUTHOR' };

console.log(Role.ADMIN);

You can access enum by using the dot operator with enum name.

Enums are then converted into IEFE(Immediately Invoked Function Expression), just look at app.js.

// TypeScript's enum conversion to JavaScript's IEFE
var Role;

(function (Role) {
    Role["ADMIN"] = "ADMIN";
    Role["READER"] = "READER";
    Role["AUTHOR"] = "AUTHOR";
}) (Role || (Role = {}));

console.log(Role.ADMIN);

At the backend, enums are mapped with numeric values. By default, the numeric values of enums start with 0 index but they can also be set to any value.

8. any: If you want no restrictions on data types and want to store any value, then this is used.

let a: any = 'AfterAcademy' // string

console.log(a);

a = 2; // integer

console.log(a);

9. void: void is a return type that can be changed with different types. A void is used when we are returning nothing from functions. This means that functions do not return anything.

// void as return type
function greeting(): void {  
   console.log("Hello, AfterAcademy!");
}

greeting();

10. never - It represents values which will never occur. It is used in functions that can never return any value and with variables that have impossible types. This type is introduced in version 2.0.

// function that never returns
function infinteLoop(): never {
   while(true) {}
}

// variable with impossible type
function impossibleType(value: any) { 
   if ( typeof value === "boolean" && typeof value === "number" ) 
      value;     // Type never  
}

It is rarely used in a function or variable with the explicit type of never but it is important to know that this type exists. These are mostly used with error handling.

Type Inference

It is yet another feature of Typescript. Even if we do not define a variable with a specific type, TypeScript infers the type with the value with which we have initialized the variable. Moreover, Typescript assigns "any" as a type to a variable which is uninitialized in our code.

var anyVariable;  // any type
var numVariable = 10; // number
var strVariable = 'Hello, AfterAcademy'; // string
var blnVariable = true; // boolean
var stringArrayVariable = ['After', 'Academy']; // string[]

Functions

Typescript Functions are mostly the same as Javascript functions and do not make any major changes to the function-scoped core of JavaScript. However, Typescript forces us to declare strong signatures with strict parameter types and return types.

Declaring a function with the function keyword is similar here as in Javascript. But the signature must be declared in the following manner.

function functionName(arg1: string, arg2: number): returnType {}

Remember that the return type is not mandatory.

Let's look at an example:

// function to add two numbers
function add(num1: number, num2: number): number {
   return num1 + num2;
}

In a similar fashion, you can declare arrow functions as well.

Function as Types

Apart from the above-mentioned types, we can even set a variable to a "function type".

Let's take the same example:

function add(num1: number, num2: number) {
   return num1 + num2;
}

Now let's declare a variable and assign it to a function by using the Function keyword.

// assigning function to a variable
let merge: Function;
merge = add;

Here, the merge is pointing to add() function, but it is not strict. merge can point to any other function as well, but only functions are allowed. We can even make a variable to point to a particular function only, by using function types. The function type is a type that describes a function regarding the parameters and the return value of that function.

A function type is created with the arrow function notation. The syntax for declaring a function type is:

// Declaring function type
let variableName: (arg1: type, arg2: type) => returnType;

Let's take the above example, in which we will make merge() to point to an add() function only.

let merge: (a: number, b: number) => number;

Here, merge() will point to a function that takes two number arguments and has a number as the return type.

Type Aliases

This is another Typescript feature that allows you to describe the structure of an object or a function signature. These are used with the help of type keyword.

Let's say we want to declare multiple arrays, each of which could contain the string, number or boolean elements only and traverse the elements. We have to declare all the arrays with the specified type of elements that will get stored in it.

The code will look something like this:

var arr1: Array<string | boolean | number> = ["After", "Academy"];
var arr2: Array<string | boolean | number> = [2, "Academy"];
var arr3: Array<string | boolean | number> = [true, 4];

function arrTraverse(myArr: Array<string | boolean | number>) {
   myArr.forEach(element => {
      console.log(element);
   });
}

arrTraverse(arr1);
arrTraverse(arr2);
arrTraverse(arr3);

Every time we create an array, we must specify the type of element it is going to store. This is time-consuming and looks not so good. This is where type aliases come handy. Now let's look at how type aliases solves this problem.

type AllTypeArray = Array <string | boolean | number> ;

var arr1: AllTypeArray = ["After", "Academy"];
var arr2: AllTypeArray = [2, "Academy"];
var arr3: AllTypeArray = [true, 4];

function arrTraverse(myArr: AllTypeArray) {
   myArr.forEach(element => {
      console.log(element);
   });
}

arrTraverse(arr1);
arrTraverse(arr2);
arrTraverse(arr3);

The code looks clean and is less error-prone.

TypeScript Classes

TypeScript classes are very similar to classes in other object-oriented programming languages. That is, a class is considered to be the blueprint of objects. They tell us how objects are going to look like. A class can also inherit other classes and even interfaces to extend its functionality. This is what we call Inheritance. A class can be defined with the keyword “class” followed by class name and contain constructor, fields, and functions. Like other object-oriented languages, we can define the scope of a variable inside classes as "public" or "private". Remember that javascript does not contain these keywords, so there is no way we can distinguish between the public and private fields once it is converted to javascript.

Classes also contain a very special method that is invoked as soon as the object gets created and that method is called the constructor. Both in Typescript and javascript, we define a constructor with the keyword "constructor".

The syntax for creating a class is:

// good practise to capitalize the first letter
class ClassName {  
   // class scope 
}

Let's take an example by creating a Department class :

// 'Department' class declaration 
class Department {           
   name: string;               // field name
   constructor(n: string) {    // constructor function
      // referencing current object with 'this' keyword
      this.name = n;
   }
}

// creating object with 'new' keyword 
const computerScience = new Department('Computer Science');
 
console.log(computerScience);  // logging the object

TypeScript Interfaces

Interfaces describe the structure of an object. They exist to perform type checking during compile time. So Typescript compiler with the help of interface checks if variables have the structure defined by the interface. Unlike other programming languages, TypeScript does not require an object to have the exact same structure as defined by the interface rather objects can have any shape but they must define the functions and properties that are present inside the interface that they implement. We create an interface in Typescript with the help of interface keyword which does not exist in vanilla javascript.

Let's look at an example:

interface Person {
    name: string;
    age: number;
}

Here, a Person is defined with the name and age property. Unlike classes, we cannot initialize these properties there. We can also add methods inside the interface.

interface Person {
    name: string;
    age: number;
    // method that takes a string and return type is void
    greet(message: string): void; 
}

But only method structure is allowed, not a concrete definition. Now let's use it to Typecheck an object. Let's create a variable user1 and store Person structure in it.

let person1: Person;

Now let's assign a value to user1. That value must be an object and that object must contain the name, the age property and a greet() method that takes a message argument and returns nothing and that is because we have already defined the structure for this variable.

person1 = {
   name: 'John Doe',
   age: 30,
   greet(message: string) {      // implementaion of greet()
      console.log(message + ' ' + this.name);
   }   
};

So, this will be a valid user as it satisfies the definition of our interface.

Type Aliases vs Interface

Now that we know both type aliases and interfaces, we will now distinguish between the two. Many of you might have some doubts regarding when to use what.

So, generally, people when code object-oriented concepts, go for the interface. This is because the interface resembles class. In other object-oriented languages like Java, interfaces are highly used to perform multiple inheritance and code abstractions. While, when people write functional code they choose type aliases over the interface due to its added advantage to support primitive types.

Type aliases can be used with any kind of type including primitive, union or intersection type. The interface on the other hand can be used with the named object type.

For example, the following statements are valid:

// primitive
type Name = string;

// object
type X_Coordinate = { x: number; };
type Y_Coordinate = { y: number; };

// tuple
type Data = [number, string];

Another difference is that type aliases cannot be extended or implemented if you use the union operator with your type definition.

For example:

type Person = { name: string; } | { age: number; };

// Error: can not implement a union type
class Student implements Person {
   name = "John Doe";
   age = 30;
}

Also, type alias does not create a new name for instance while an interface can create a new name that can be used everywhere.

Finally, declaration merging is supported by interfaces but not by type aliases. It means all fields that are declared in multiple declarations of the same interface, will get merged into a single interface.

// These two declarations become:
// interface Person { name: string; age: number; }
interface Person { name: string; }
interface Person { age: number; }

const person: Person = { name: "John Doe", age: 30 };

These are some differences that will surely help you in understanding more about interface and type aliases.

Inheritance

TypeScript allows you to inherit a class from existing classes. By doing that you can extend their functionality as per your requirement. This feature is available in all object-oriented languages including modern javascript. In Typescript, this is done with the help of extends keyword.

Let us look at an example in which we have a class named Person and another class called Student, created by extending the Person class.

// declaring a Person class
class Person {
   firstName: string;
   lastName: string;
   constructor(firstName: string, lastName: string) {
      this.firstName = firstName;
      this.lastName = lastName;
   }
}

// declaring Student class that inherits Person class
class Student extends Person {
   department: string;
   rollNumber: number;
   constructor(firstName: string, lastName: string, department:   
      string, rollNumber: number) {
      // call to Person's constructor
      super(firstName, lastName);  
      this.department = department;
      this.rollNumber = rollNumber;
  }
}

// creating Student object, it will contain properties of both classes
var obj = new Student("John", "Doe", "CSE", 10);

Here, The Person class is called Base class and the Student class is called Derived class.

Modules

Whenever the code base grows it becomes important to organize classes and interfaces for better maintainability and easy debugging. TypeScript modules allow you to achieve that. A module acts as a container for the code and helps to organize your code in a clean way. The modules are created with the help of the module keyword. Let's see how to create a module.

Have a look at the following code that defines a module named Corporate.

module A {
// to access this class outside 'A' module, use export
   export class B { 

   }
   class C {
      
   }
}

var obj = new A.B();

The above code defines an 'A' module containing 2 classes - B and C. All the classes from a module are accessible only within that module, by default. Here the export keyword is used to access the 'B' class from outside the 'A' module.

We are now done. Together we have completed the basics of Typescript, we will dive deep in Typescript in coming tutorials.

Closing Notes

We now have the basic knowledge of Typescript, why it is created, how to declare variables and functions, what is meant by types in Typescript and what are its different types. We will learn much more incoming tutorials. Till then, try to make some basic projects in Typescript.

A good way to learn Typescript is by converting a project you’ve written in Javascript to Typescript. If you don’t know where to start, learn from our open source project - Node.js Backend Architecture Typescript and contribute to it. In this way, you will learn much faster.

Keep Learning :)

Team AfterAcademy!!