Introduction to Typescript
Typescript is an open source language developed by Microsoft and is a superset of Javascript. Let's have a look at its
key features and how to install it and configure it in order to run our first typescript app.
Key Features:
- Easy to configure
- Get translated into nice Javascript
- Syntax and Type similar to Java
- Let and const, function scope variable and Immutable variable
- Arrow Expressions
- For of / For in
- Class and Interfaces
- Access Modifiers
- Module
- Ambient Declaration
- Decorators / Annotations
Easy to configure
You can install typescript by downloading it from the npm repository.
npm install -g typescript
If you don't know if you have npm and Node.Js installed, run the following commands in your terminal to check it:
node -v in
npm -v
If you don't have Node.js and npm, download it from here: https://nodejs
.org/en/download/, or Mac users can use brew :
brew install node
Ubuntu users can use apt-get:
curl -sL https://deb.nodesource.com/setup_5.x | sudo -E bash -
sudo apt-get install -y nodejs
To test if the environment is fully working, clone the following typescript sample project from github https://github.com/magemello/typescript-sample-project
or copy and paste the following class in a .ts file
helloworld.ts
function sayHello(name) {
return "Hello World! I'm " + name;
}
let user = "Mario Romano";
document.body.innerHTML = sayHello(user);
then from the terminal run the typescript compiler, and if everything is setup correctly you should see a .js file appear:
tsc helloworld.ts
Now your folder contains 2 helloworld files, a .ts and .js. Great, the next step is easy. To show
the content of our hello world file inside a browser we need a .html that includes the generated
.js file.
index.html
<!DOCTYPE html>
<html>
<head><title>TypeScript sample project</title></head>
<body>
<script src="helloworld.js"></script>
</body>
</html>
Get translated into nice Javascript
Typescript is compiled in nice readable Javascript. Because of the compiler there are two levels of errors:- Compiler errors (example 2)
- Runtime errors (example 3)
//Javascript (1)
var testVar = 123;
testVar.trim();
//Uncaught TypeError: testVar.trim is not a function(…)
//Typescript (2)
let testVar : string = 123;
testVar.trim();
//Compiler error: Type 'number' is not assignable to type 'string'.
//Typescript (3)
let testVar : any = 123;
testVar.trim();
//Runtime error: ORIGINAL EXCEPTION: TypeError: testVar.trim is not a function
The compilers setting are defined in the tsconfig.json. The tsconfig.json file specifies the root files and the compiler options required to compile the project. The option ModuleResolution has 2 different strategies: Classic or Node, by default is node. I recommend to use node, classic its there just for backward compatibility.
tsconfig.json
{
"compilerOptions": {
"target": "es5",
"module": "commonjs",
"moduleResolution": "node",
"emitDecoratorMetadata": true,
"experimentalDecorators": true,
"sourceMap": true,
"removeComments": true,
"declaration": true,
"outDir": "dist"
},
"exclude": [
],
"files": [
],
"filesGlob": [
]
}
Syntax and Type similar to Java
Typescript is easy to learn for a java developer, also because there are some resemblances between their types.
Java Typescript
Object ====> any
void ====> void
boolean ====> boolean
array[] ====> array[]
String
char ====> string
int
long ====> number
short
Let and const, function scope variable and Immutable variable
In Typescript is not recommended to declare variables using var. To declare a variable you can use let or const.Now let's see what is the difference between let that is block scope and var that is function scoped:
//Javascript (1)
var people = 100;
if (people === 100) {
var people = 200;
//output: 200
console.log(people);
}
//output: 200
console.log(people);
//Typescript (2)
let people = 100;
if (people === 100) {
let people = 200;
//output: 200
console.log(people);
}
//output: 100
console.log(people);
Const is block scope as well and is also immutable, so after the initialization you can not change the value of the const elements, let's see what it means:
const people = 123;
if (people === 123) {
const people = 456; // Allowed as its block scoped
}
people = 456; // ERROR: Left-hand side of an assignment expression cannot be a constant
Arrow function / Lambda
- You don't need to keep typing function
- It lexically captures the meaning of this
- It lexically captures the meaning of arguments
//Arrow function/lambda
let filter: (peopleName: string[], name: string) => string = function (peopleName, name) {
for (var currentName of peopleName) {
if (currentName === name) {
return name;
}
}
}
//Output: mario
console.log(filter(['mario', 'luigi', 'wario'], 'mario'));
//Arrow function/lambda less verbose
let filter2 = (peopleName: string[], name: string) => {
for (var currentName of peopleName) {
if (currentName === name) {
return name;
}
}
}
//Output: mario
console.log(filter2(['mario', 'luigi', 'wario'], 'mario'));
Functions with => operator keep the outside context, so you don't have to do anymore hack like "self = this" to keep the
context.
//Javascript
function People() {
this.people = 100;
var self = this;
this.addOne = function() {
self.people++;
}
}
var community = new People();
setTimeout(community.addOne, 1000);
//Output: 100
setTimeout(function() {
console.log(community.people);
}, 2000);
//Typescript
class People {
people:number = 100;
addOne = () => {
this.people++;
}
}
var community = new People();
setTimeout(community.addOne, 1000);
//Output: 101
setTimeout(function() {
console.log(community.people);
}, 2000);
For of vs For in
A nice add of EC6 that you can use also in Typescript is the "for of" that iterates over the value of an array instead of the old "for in" that iterates over the key:
const people1 = ['mario', 'luigi', 'wario'];
for (let key in people1) {
//Output: 0, 1, 2
console.log(key);
}
const people2 = ['mario', 'luigi', 'wario'];
for (let person of people2) {
//Output: mario, luigi, wario
console.log(person);
}
Class and Interfaces
EC6 an Typescript have introduced classes. Classes are important to structure the code in elegant object oriented way.
class car {
color: string;
setColor(color: string): void {
this.color = color;
}
printColor(): void{
console.log(this.color);
}
}
Defining an Interface we can define a common behaviour for our classes and be more explicit about the functionality they provide. A key difference between Typescript and Java is that compatibility of interfaces is determined by the properties on the interface, rather than by the inheritance chain.
//Interface
interface Cars {
color: string;
setColor(color: string): void;
}
//Implements the interface
class Ferrari implements Cars {
color: string;
setColor(color: string): void {
this.color = color;
}
printColor(): void {
console.log(this.color);
}
}
//Execution
let supercar = new Ferrari();
supercar.setColor('red');
//Output: red
supercar.printColor();
You can also define abstract Class
//The abstract class
abstract class Ferrari {
color: string;
setColor(color: string): void {
this.color = color;
}
abstract printColor(): void;
}
//Implements the abstract class
class FerrariGto implements Ferrari {
color: string;
setColor(color: string): void {
this.color = color;
}
printColor(): void{
console.log(this.color);
}
}
let gto = new FerrariGto();
gto.setColor('yellow');
//Output: yellow
gto.printColor();
Or simply extend a class
//Original class
class FerrariGto {
car: string = 'Ferrari';
color: string;
setColor(color: string): void {
this.color = color;
}
getColor(): string {
return this.color;
}
printName(): void{
console.log(this.color);
}
}
//Extend class
class FerrariGtoSport extends FerrariGto {
color: string;
printName(): void{
console.log(this.car + " " + this.getColor() + " sport");
}
}
let gtoSport = new FerrariGtoSport();
gtoSport.setColor('red');
//Output: Ferrari red sport
gtoSport.printName();
Access Modifiers
With classes of course came also access modifiers. Be aware that the access modifiers are not translated to javascript, but you can still get benefit out of it at compile time.Modifier | public |
private |
protected |
---|---|---|---|
class instances | yes | no | no |
class | yes | yes | yes |
class children | yes | no | yes |
Module - Import and Export
When you write code by default it is in the global scope, so how do we define scopes in typescript?We use the import and export keyword to do that. The export keyword creates a local scope that can be stored in a separate file. But now you need to include this external file, to do that you can use the import keyword. To load this module at runtime you have to use a module loader, you can choose between :
- CommonJS, server side
- SystemJS, wrap CommonJS for in browser use
- Require.js (AMD) for in browsers use
- Isomorphic (UMD)
- ECMAScript 2015 native modules (ES6)
gto.ts
export class FerrariGto {
color: string;
setColor(color: string): void {
this.color = color;
}
printColor(): void{
console.log(this.color);
}
}
gto-sport.ts
import { FerrariGto } from "./gto.ts"
export class FerrariGtoSport extends FerrariGto {
color: string;
setColor(color: string): void {
this.color = color;
}
printColor(): void{
console.log(this.color + " sport");
}
}
If you want to check an example where is utilized SystemJS, you can clone this Github project from the official typescript
repository: SystemJS and Typescript.
Ambient Declarations
One of the first problems that you may have is how to use an old library written in javascript with typescript?TypeScript allows you to use existing JavaScript libraries in TypeScript using Ambient declarations. Let's say for example that in our index.html we are importing jQuery and we want to use it inside our .ts file, this is how to do it:
index.html
<!DOCTYPE html>
<html>
<head><title>TypeScript sample project</title></head>
<body>
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.12.2/jquery.min.js"></script>
<script src="helloworld.js"></script>
</body>
</html>
helloworld.ts
declare var $;
function sayHello(name) {
return "Hello World! I'm " + name;
}
let user = "Mario Romano";
$('body').html(sayHello(user));
Decorators / Annotations
Decorators use the form @expression, what a decorators does is add at runtime functionality to the annotated element. Decorators can be applied on:- Class
- Method
- Accessor
- Property
- Parameter
- Metadata
@sealed
class Greeter {
private _x: number;
greeting: string;
@format("Hello, %s")
greeting: string;
constructor(message: string) {
this.greeting = message;
}
@enumerable(false)
greet() {
return "Hello, " + this.greeting;
}
@validate
greetHello(@required name: string) {
return "Hello " + name + ", " + this.greeting;
}
@configurable(false)
get x() {
return this._x;
}
}
Typings
Typings is the simple way to manage and install TypeScript definitions. It uses typings.json
{
"ambientDependencies": {
"es6-shim": "github:DefinitelyTyped/DefinitelyTyped/es6-shim/es6-shim.d.ts#7de6c3dd94feaeb21f20054b9f30d5dabc5efabd",
"jasmine": "github:DefinitelyTyped/DefinitelyTyped/jasmine/jasmine.d.ts#5c182b9af717f73146399c2485f70f1e2ac0ff2b"
}
}