Click here to Skip to main content
65,938 articles
CodeProject is changing. Read more.
Articles / Languages / ES6

Learn With Me - Typescript: A Study in Types

4.75/5 (6 votes)
5 Apr 2016CPOL9 min read 25.9K   62  
A discussion on TypeScript's type system in detail

Other Parts

Introduction

In the last few years as technology advanced, the expectation of users increased the need for web applications with rich user experience. To cater to this demand, JavaScript development has been increased exponentially. To be frank, JavaScript was not designed to support modern days' large scale and complex development. Hence, it comes with an expensive maintainability issue. Typescript, an open source language project initiated by Microsoft, was developed to take JavaScript to the next level and overcome these issues. One of the major aspects of TypeScript is that it is a strong typed language. In this article, we will discuss about the type system of TypeScript.

Background

JavaScript was originally created as a scripting language, but still it is versatile to adopt the object oriented programming. Typescript provides easier ways to access to those features by introducing some object oriented concepts:

  • Static typing
  • Classes
  • Interfaces
  • Generics
  • Modules

TypeScript is a statically typed compiled language adding a static typing layer on top of JavaScript. When compiled, the compiler parses the TypeScript and outputs plain JavaScript. Being an interpreted language, debugging in JavaScript is only possible in runtime. But, the compilation allows TypeScript to raise errors before deploying the codes.

As TypeScript is a superset of JavaScript, any valid JavaScript code is also a valid TypeScript code. In other words, all JavaScript code is TypeScript code but TypeScript code is not essentially JavaScript. So, an existing JavaScript application can easily be converted to a typescript application without much hassle.

Tools and IDE

As a Microsoft initiative, TypeScript comes as a built in feature with Visual Studio 2015. But for the developers who do not use Visual Studio, there is an npm module available for free. Those who are new to the term, npm is a package manager. You can learn more from https://www.npmjs.com. To use npm, you need to have node.js installed in your computer. If you do not have it, go and get it from https://nodejs.org

Once you have node.js installed, open the Node.js command prompt and type in the command:

npm install -g typescript

If it is installed successfully, well done! You will now be able to compile TypeScript files to JavaScript files.

Along with Visual Studio, some other popular text editors have TypeScript plugins. I am using Atom (https://atom.io) with atom-typescript plugin (https://atom.io/packages/atom-typescript).

Type System Basics

As its name suggests, TypeScript introduced optional types to the JavaScript variable and made itself a strongly typed programming language. TypeScript's primitive types are:

  • undefined
  • null
  • void
  • boolean
  • number
  • string

All types are subtypes of a a super type, any. TypeScript types also includes array<font color="#111111" face="Segoe UI, Arial, sans-serif"><span style="font-size: 14px;">,</span></font>enum. Using strong types are a proven technique for increasing code quality and readability. It is also useful to help the compiler catch the errors in compile time rather than breaking the application while it is running.

Variable Inference

To provide type safety with minimum cost of effort, TypeScript infers as much as possible. Once inferred, the variable becomes a strongly typed variable of the inferred type. So, you will not be allowed to do anything to the variable you are not allowed to do with the inferred type.

In your work folder, create a new file named types.ts. Put the following line in the file and save.

JavaScript
//Inference

var test = 0;

Now it is time to compile it. Open up the Node.js command prompt, navigate to your project folder and tell TypeScript Compiler to compile types.ts file:

tsc types.ts

A new JavaScript file with the same name as the TypeScript file will be created in the same directory. Open the .js file, it should contain the same content of the .ts file.

Now, append the following line to the types.ts file and compile.

JavaScript
test = "I am a string";

You will be provided with an error message by the compiler saying "Cannot convert 'string' to 'number'." 

Hmm, so finally, TypeScript is showing its colour. What is nicely executable in JavaScript, is not compilable in TypeScript. In the first line, test was inferred as a number type variable. When we wanted to assign a string to a number type variable, it had no other way than to complain.

Optional Static Type Notation

Sometimes, the variable inference is not possible due to the nature of the business flow. But even then, in TypeScript, we are able to define the type of the variable through Optional Static Type Notation. Once you declare a static type notation for a variable, it cannot be assigned with values of any other type.

Open up the types.ts file and comment out the line that was causing trouble. Add the following lines of code to end of types.ts file, save and in the Node.js command prompt, enter the command to compile it.

JavaScript
//...

var num: number;
var str: string;

num = 0;
str = "Hello World!";

Once it is compiled, open the .js file and have a look. It is our very familiar JavaScript syntax without any "type notation". Now, add the following snippet in bold and compile it again.

JavaScript
//...
var num: number;
var str: string;

if(1 != 2)
{
  num = "This won't compile"; //Cannot convert 'string' to 'number'
}

num = 0;
str = "Hello World!";

Huh, compilation error... Pretty cool, eh!

Some Interesting Types

Any Type

When a variable is declared without the inference or without any sort of type notation, the variable gets declared with any type. And any type variable acts the same as any standard JavaScript variable and can be assigned to any value. Any type gives us a strong platform to convert the existing javascript codes to TypeScript.

To have a hint of what any type is, comment out the if block from the previous code snippet and type in the following lines in the types.ts file. Compile it.

JavaScript
//Any
var anything: any; //explicit any
var implicit_anything; //implicit any

anything = -1;
anything = [1, 2, 3]; //No problem
anything = "This will be compiled";

implicit_anything = -1;
implicit_anything = [1, 2, 3]; //No problem
implicit_anything = "This will be compiled too";

Array Type

TypeScript allows us to declare an array in two ways. Declaring with square brackets and declaring as generic Array type.

JavaScript
//Array
var arr1: number[] = [1, 2, 3];
var arr2: Array<number> = [1, 2, 3];

Enum Type

Enums are cool ways to name the numbers in TypeScript like in any other language. To use an enum:

JavaScript
//Enum
enum MyEnum1 {Val1, Val2, Val3};
var enumVal1: MyEnum1 = MyEnum1.Val1;

enum MyEnum2 {Val1 = 11, Val2, Val3};
var enumVal2: MyEnum2 = MyEnum2.Val2;

By default, the starting value of an enum is always zero if not declared otherwise. In the example, the value assigned to enumVal2 is 0. But enumVal2 is assigned with 12.

Void Type

If we return nothing from a function, the function is said to return a void type. In other words, void means nothing. In the world of TypeScript, there is no existence of any value of void type. We can use type notation for void type, but it does not have any implication. But like any other type, once we use type notation on a variable as void, it cannot be assigned with values of another type.

To start with void type, let's add  the following lines in the types.js file and compile.

JavaScript
//Void
var returnVal: void

returnVal = 1;

As I said, "Type 'number' is not assignable to type 'void'." If we remove the last line, it will be compiled.

Union Type

TypeScript allows us to have Union types as well. A union type variables can store value of more than one types as declared. If we declare a union type variable to contain number or string or an array of strings, it will contain any value of those types. But, if we try to assign some other type of value, say boolean, it will throw a compilation error. Let's give it a go. Type in the following lines in types.ts file.

JavaScript
//Union Type
var unionVariable: number | string | string[];
unionVariable = 2;
unionVariable = 'Hello World';
unionVariable = ["Hello", "World"];
unionVariable = true; //Error

Save and compile. "Type 'boolean' is not assignable to type 'number | string | string[]'."

Now remove the last line and try. It will be compiled and the .js file will be created.

Anonymous Type

A variable can be declared as an anonymous type variable. Add the code to the end of types.ts file. Uncomment the commented out line and compile.

JavaScript
var v: {id: number, value: string};

//v = {value: 1, id: "A"} //Error
v = {value: "A", id: 1};

If an anonymous type variable is tried to be assigned with a value which does not match with the structure we declared as our anonymous type, we will be provided with errors by the compiler.

Advanced Type Topics

As we are now familiar with the types in TypeScript, we may proceed to the next level - some advanced topics regarding the type system.

Type Alias

With this feature on board, we can define aliases for the variables and use it alternatively. It comes in really handy if you want to work with the union types. Rather than putting a blob of types a variable can support, we can define an alias now. Let's define an alias for our previously declared union and try to do the same operations.

JavaScript
//Type Alias
type myUnion = number | string | string[];

var myUnionVariable: myUnion;
myUnionVariable = 2;
myUnionVariable = 'Hello World';
myUnionVariable = ["Hello", "World"];
//myUnionVariable = true; //Error

Scopes

TypeScript allows us to have scopes for variables and constants. In TypeScript, we can use var or let to declare a variable. const is used to create a constant. All of these can be scoped to global level if declared outside of any code blocks (enclosed with curly braces). If done so, they can be accessed from anywhere. Apart from that:

var declares variables scoped local to the functions, which means if a var is declared in anywhere in a function, it can be used anywhere in that function.

let is used to declare variable scoped to the nearest enclosure. This enables us to scope a variable to any block enclosed by curly braces and make it unaccessible from outside of that block.

Constants declared with const have the same scoping behaviour as variables declared with let. They can be used at global, local or block level. As we are in this, the type aliases also behave the same.

To play with scopes, let's open the types.ts file and include the following lines:

JavaScript
var globalVar: number = 0;
let globalLet: number = 0;
const globalConst : number = 0;
function myFunction (){
  var localVar: number = 1;
  let localLet: number = 1;
  const localConst : number = 1;

  {
    var blockVar: number = 1;
    let blockLet: number = 1;
    const blockConst : number = 1;

    type blockTypeAlias = number | string | string[];
  }

  globalVar = 2;
  globalLet = 2;
  alert(globalConst);
  localVar = 2;
  localLet = 2;
  alert(localConst);
  blockVar = 2;
  //blockLet = 2; //Error
  //alert(blockConst); //Error
  //var blockTypeVar: blockTypeAlias; //Error
}

globalVar = 3;
globalLet = 3;
alert(globalConst);
//localVar = 3; //Error
//localLet = 3; //Error
//alert(localConst);
//blockVar = 3; //Error
//blockLet = 3; //Error
//alert(blockConst); //Error

Uncomment the commented out lines randomly and try to compile.

Casting

Let's begin with the following code snippet:

JavaScript
type type1 = {id: number, name: string}
type type2 = {name: string, id: number}

var v1: type1 = {id: 2, name: 'abc'}
//var num = <number> v1; //Error
var v2 = <type2> v1;

//v2 = 2; //Error
v2 = <any> v2;
var v3 = <any> v2;
v3 = 2;

Seems like TypeScript variables can be casted to a super type or another type with similar properties. Now, uncomment the commented out lines and compile. You will be provided with errors. For this compile time verification, the TypeScript casting is said to be "type assertion" rather than casting.

Type Guards

TypeScript allows us to assign values of multiple types in a single variable through any and union type variables. It also allows us to execute statements specific for a targeted type using javascript's typeof and instanceof operators within a conditional block. This technique is called Type Guard.

JavaScript
var a: any;

if(typeof a === "number")
{
  //a.toLowerCase(); //Error
}

if(typeof a === "string")
{
  a.toLowerCase(); //No Problem
}

a.toLowerCase();

type t = number | string;
var b: t;

if(typeof b === 'string')
{
    b.toLowerCase();
}

//b.toLowerCase(); //Error

Add the code snippet in our already famous types.ts file and play with it by uncommenting the commented out lines.

Conclusion

We had a detailed discussion on the type system of TypeScript. Next time, we will learn about the functions and know how to work with them.

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)