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.
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.
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.
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.
var num: number;
var str: string;
if(1 != 2)
{
num = "This won't compile";
}
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.
var anything: any;
var implicit_anything;
anything = -1;
anything = [1, 2, 3];
anything = "This will be compiled";
implicit_anything = -1;
implicit_anything = [1, 2, 3];
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.
var arr1: number[] = [1, 2, 3];
var arr2: Array<number> = [1, 2, 3];
Enum Type
Enum
s are cool ways to name the numbers in TypeScript like in any other language. To use an 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.
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 string
s, 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.
var unionVariable: number | string | string[];
unionVariable = 2;
unionVariable = 'Hello World';
unionVariable = ["Hello", "World"];
unionVariable = true;
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.
var v: {id: number, value: string};
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.
type myUnion = number | string | string[];
var myUnionVariable: myUnion;
myUnionVariable = 2;
myUnionVariable = 'Hello World';
myUnionVariable = ["Hello", "World"];
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:
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;
}
globalVar = 3;
globalLet = 3;
alert(globalConst);
Uncomment the commented out lines randomly and try to compile.
Casting
Let's begin with the following code snippet:
type type1 = {id: number, name: string}
type type2 = {name: string, id: number}
var v1: type1 = {id: 2, name: 'abc'}
var v2 = <type2> v1;
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.
var a: any;
if(typeof a === "number")
{
}
if(typeof a === "string")
{
a.toLowerCase();
}
a.toLowerCase();
type t = number | string;
var b: t;
if(typeof b === 'string')
{
b.toLowerCase();
}
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.