/ Technology

Hunger Games Guide to the Typescript

Introduction

TypeScript is an open source programming language that was developed to help enable building large-scale JavaScript applications. TypeScript adds universal concepts such as classes, modules, interfaces, generics and (optional) static typing to JavaScript.
TypeScript is syntactic sugar for JavaScript whose syntax is a superset of ECMAScript 2015 syntax. Every JavaScript program is also a TypeScript program and can be added easily to the program.
The TypeScript compiler emits JavaScript.

To test TypeScript’s compiler, head to the Playground where you will be able to type code, have auto-completion and directly see the emitted JavaScript.

Variable Declarations

Let

let is similar to var in some respects, but allows users to avoid some of the common “gotchas” that users run into in JavaScript.

Typescript:

let fullName: string = `Bob Bobbington`;
let age: number = 37;

Javascript:

var fullName = "Bob Bobbington";
var age = 37;

Const

const is an augmentation of let in that it prevents re-assignment to a variable.

Typescript:

// Use const keyword for constants
const numHands = 2;
numHands = 4; // Error

Javascript:

// Use const keyword for constants
var numHands = 2;
numHands = 4; // Error

Basic Types

For programs to be useful, we need to be able to work with some of the simplest units of data: numbers, strings, structures, boolean values, and the like.
There are three basic types in TypeScript:

Boolean

The most basic datatype is the simple true/false value, which JavaScript and TypeScript call a boolean value.

Typescript:

let isDone: boolean = false;
// But you can omit the type annotation if the variables are derived from explicit literals
let isDoneWo = false;

Javascript:

var isDone = false;
// But you can omit the type annotation if the variables are derived from explicit literals
var isDoneWo = false;

Number

As in JavaScript, all numbers in TypeScript are floating point values. These floating point numbers get the type number.

Typescript:

let decimal: number = 6;
// But you can omit the type annotation if the variables are derived from explicit literals
let decimalWo = 42;

Javascript:

var decimal = 6;
// But you can omit the type annotation if the variables are derived from explicit literals
var decimalWo = 42;

String

Another fundamental part of creating programs in JavaScript for web pages and servers alike is working with textual data.

Typescript:

let color: string = "blue";
// But you can omit the type annotation if the variables are derived from explicit literals
let colorWo = "blue";

Javascript:

var color = "blue";
// But you can omit the type annotation if the variables are derived from explicit literals
var colorWo = "blue";

Template String

You can also use template strings, which can span multiple lines and have embedded expressions. These strings are surrounded by the backtick/backquote (`) character, and embedded expressions are of the form ${ expr }.

Typescript:

// Template Strings (strings that use backticks)
// String Interpolation with Template Strings
let name = 'Tyrone';
let greeting = `Hi ${name}, how are you?`
// Multiline Strings with Template Strings
let multiline = `This is an example
of a multiline string`;

Javascript:

// Template Strings (strings that use backticks)
// String Interpolation with Template Strings
var name = 'Tyrone';
var greeting = "Hi " + name + ", how are you?";
// Multiline Strings with Template Strings
var multiline = "This is an example\nof a multiline string";

Any

We may need to describe the type of variables that we do not know when we are writing an application. In short, when it's impossible to know, there is the "Any" Type.

Typescript:

let notSure: any = 2;
notSure = "maybe a string value instead";
notSure = true; // definitely a boolean

Javascript:

var notSure = 2;
notSure = "maybe a string value instead";
notSure = true; // definitely a boolean

Array

TypeScript, like JavaScript, allows you to work with arrays of values.

Typescript:

// For collections, there are typed arrays and generic arrays
let typedList: number[] = [1, 2, 3];
// Alternatively, using the generic array type
let genericList: Array<number> = [1, 2, 3];

Javascript:

// For collections, there are typed arrays and generic arrays
var typedList = [1, 2, 3];
// Alternatively, using the generic array type
var genericList = [1, 2, 3];

Enums

A helpful addition to the standard set of datatypes from JavaScript is the enum. As in languages like C#, an enum is a way of giving more friendly names to sets of numeric values.

Typescript:

// For enumerations:
enum Color { Red, Green, Blue }
let c: Color = Color.Green;

Javascript:

// For enumerations:
var Color;
/* Map of Color
 Set Color["Red"] = 0 and then set Color[0] = "Red"
 Note: "Color["Red"] = 0" returns 0
Color =
0:"Red"
1:"Green"
2:"Blue"
Blue:2
Green:1
Red:0
*/

(function (Color) {
    Color[Color["Red"] = 0] = "Red";
    Color[Color["Green"] = 1] = "Green";
    Color[Color["Blue"] = 2] = "Blue";
})(Color || (Color = {}));
var c = Color.Green;

Void

void is a little like the opposite of any: the absence of having any type at all. You may commonly see this as the return type of functions that do not return a value:

Typescript:

// Lastly, "void" is used in the special case of a function returning nothing
function warnUser(): void {
    alert("This is my warning message");
}

Javascript:

// Lastly, "void" is used in the special case of a function returning nothing
function warnUser() {
    alert("This is my warning message");
}

Declaring variables of type void is not useful because you can only assign undefined or null to them:

Typescript:

let unusable: void = undefined;

Javascript:

var unusable = undefined;

Note: What exactly does void 0 do?

// Outputs: undefined
console.log(void 0);

Never

The never type represents the type of values that never occur. For instance, never is the return type for a function expression or an arrow function expression that always throws an exception or one that never returns; Variables also acquire the type never when narrowed by any type guards that can never be true.

Typescript:

// Function returning never must have unreachable end point
function error(message: string): never {
    throw new Error(message);
}

// Inferred return type is never
function fail() {
    return error("Something failed");
}

// Function returning never must have unreachable end point
function infiniteLoop(): never {
    while (true) {
    }
}

Javascript:

// Function returning never must have unreachable end point
function error(message) {
    throw new Error(message);
}
// Inferred return type is never
function fail() {
    return error("Something failed");
}
// Function returning never must have unreachable end point
function infiniteLoop() {
    while (true) {
    }
}

Tuple

Tuple types allow you to express an array where the type of a fixed number of elements is known, but need not be the same. For example, you may want to represent a value as a pair of a string and a number:

Typescript:

// Declare a tuple type
let x: [string, number];
// Initialize it
x = ["hello", 10]; // OK
// Initialize it incorrectly
x = [10, "hello"]; // Error

console.log(x[0].substr(1)); // OK
console.log(x[1].substr(1)); // Error, 'number' does not have 'substr'

Javascript:

// Declare a tuple type
var x;
// Initialize it
x = ["hello", 10]; // OK
// Initialize it incorrectly
x = [10, "hello"]; // Error
console.log(x[0].substr(1)); // OK
console.log(x[1].substr(1)); // Error, 'number' does not have 'substr'

Functions

Functions are the fundamental building block of any applications in JavaScript. In TypeScript, while there are classes, namespaces, and modules, functions still play the key role in describing how to do things. TypeScript also adds some new capabilities to the standard JavaScript functions to make them easier to work with.

Functions

TypeScript functions can be created both as a named function or as an anonymous function.

Typescript:

// Named function
function add(x, y) {
    return x + y;
}
// Anonymous function
let myAdd = function (x, y) { return x + y; };
// The following are equivalent, the same signature will be inferred by the
// compiler, and same JavaScript will be emitted
let square1 = function (i: number): number { return i * i; }
// Return type inferred
let square2 = function (i: number) { return i * i; }
// "Fat arrow" syntax
let square3 = (i: number): number => { return i * i; }
// "Fat arrow" syntax with return type inferred
let square4 = (i: number) => { return i * i; }
// "Fat arrow" syntax with return type inferred, braceless means no return
// keyword needed
let square5 = (i: number) => i * i;

Javascript:

// Named function
function add(x, y) {
    return x + y;
}
// Anonymous function
var myAdd = function (x, y) { return x + y; };
// The following are equivalent, the same signature will be inferred by the
// compiler, and same JavaScript will be emitted
var square1 = function (i) { return i * i; };
// Return type inferred
var square2 = function (i) { return i * i; };
// "Fat arrow" syntax
var square3 = function (i) { return i * i; };
// "Fat arrow" syntax with return type inferred
var square4 = function (i) { return i * i; };
// "Fat arrow" syntax with return type inferred, braceless means no return
// keyword needed
var square5 = function (i) { return i * i; };

Interfaces

In TypeScript, interfaces are a powerful way of defining contracts within your code as well as contracts with code outside of your project.

Typescript:

// Interfaces are structural, anything that has the properties is compliant with
// the interface
interface Person {
  name: string;
  // Optional properties, marked with a "?"
  age?: number;
  // And of course functions
  move(): void;
}

// Object that implements the "Person" interface
// Can be treated as a Person since it has the name and move properties
let p: Person = { name: "Bobby", move: () => { } };
// Objects that have the optional property:
let validPerson: Person = { name: "Bobby", age: 42, move: () => { } };
// Is not a person because age is not a number
let invalidPerson: Person = { name: "Bobby", age: true };

Javascript:

// Object that implements the "Person" interface
// Can be treated as a Person since it has the name and move properties
var p = { name: "Bobby", move: function () { } };
// Objects that have the optional property:
var validPerson = { name: "Bobby", age: 42, move: function () { } };
// Is not a person because age is not a number
var invalidPerson = { name: "Bobby", age: true };

Function Types

Interfaces are capable of describing the wide range of shapes that JavaScript objects can take. In addition to describing an object with properties, interfaces are also capable of describing function types.

Typescript:

// Interfaces can also describe a function type
interface SearchFunc {
    (source: string, subString: string): boolean;
}
// Only the parameters' types are important, names are not important.
let mySearch: SearchFunc;
mySearch = function (src: string, sub: string) {
  return src.search(sub) != -1;
}

Javascript:

// Only the parameters' types are important, names are not important.
var mySearch;
mySearch = function (src, sub) {
    return src.search(sub) != -1;
};

Readonly properties

Some properties should only be modifiable when an object is first created. You can specify this by putting readonly before the name of the property:

Typescript:

interface Point {
    readonly x: number;
    readonly y: number;
}
let p1: Point = { x: 10, y: 20 };
p1.x = 5; // error!

Javascript:

var p1 = { x: 10, y: 20 };
p1.x = 5; // error!

Classes

Starting with ECMAScript 2015, also known as ECMAScript 6, JavaScript programmers will be able to build their applications using this object-oriented class-based approach. In TypeScript, we allow developers to use these techniques now and compile them down to JavaScript that works across all major browsers and platforms, without having to wait for the next version of JavaScript.

Note:
JavaScript Object Prototypes - All JavaScript objects inherit properties and methods from a prototype.
Sometimes you want to add new properties (or methods) to all existing objects of a given type. It's done using the prototype Property.
Example:

function Person(first, last, age, eyecolor) {
    this.firstName = first;
    this.lastName = last;
    this.age = age;
    this.eyeColor = eyecolor;
}
Person.prototype.nationality = "English";

Typescript:

// Classes - members are public by default
class Point {
  // Properties
  x: number;

  // Constructor - the public/private keywords in this context will generate
  // the boilerplate code for the property and the initialisation in the
  // constructor.
  // In this example, "y" will be defined just like "x" is, but with less code
  // Default values are also supported

  constructor(x: number, public y: number = 0) {
    this.x = x;
  }

  // Functions
  dist() { return Math.sqrt(this.x * this.x + this.y * this.y); }

  // Static members
  static origin = new Point(0, 0);
}

let p1 = new Point(10, 20);
let p2 = new Point(25); //y will be 0

Javascript:

// Classes - members are public by default
var Point = /** @class */ (function () {
    // Constructor - the public/private keywords in this context will generate
    // the boiler plate code for the property and the initialization in the
    // constructor.
    // In this example, "y" will be defined just like "x" is, but with less code
    // Default values are also supported
    function Point(x, y) {
        // What exactly does void 0 do? 
        // Outputs: undefined        
        if (y === void 0) { y = 0; }
        this.y = y;
        this.x = x;
    }
    // Functions
    // Prototype adds new method to existing type
    Point.prototype.dist = function () { return Math.sqrt(this.x * this.x + this.y * this.y); };
    // Static members
    Point.origin = new Point(0, 0);
    return Point;
}());
var p1 = new Point(10, 20);
var p2 = new Point(25); //y will be 0

Advanced Techniques

Using a class as an interface

As we said in the previous section, a class declaration creates two things: a type representing instances of the class and a constructor function. Because classes create types, you can use them in the same places you would be able to use interfaces.

Note on JavaScript ES2015 Classes and Prototype Inheritance:
The extends function in JavaScript -
To support ES2015 class inheritance, TypeScript transpiles the ES2015 extends keyword functionality to a function named __extends, which executes the code needed to set up the inheritance.
Here is the code for the __extends function:

// declare a variable to reference the extends function
var __extends = 
// the extends function is already defined within the context
// of this code, so use the existing __extends function
(this && this.__extends) || 
// else
// the extends function is not already defined within the current
// context; therefore, define it
function (child, parent) {
  // mixin pattern for copying parent constructor function properties
  // as static properties to the child constructor function
  // properties on constructor function are commonly known as static properties
  for (var parentPropertyName in parent)
  {
    // only copy properties specifically defined on the parent
    if (parent.hasOwnProperty(parentPropertyName)) 
    {
      // for primitive types, this will copy the value,
      // for object types, this will copy the reference only
      child[parentPropertyName] = parent[parentPropertyName];
    }
  }
  
  // constructor function for the object that instantiated child objects
  // will inherit from
  // this function is unique within the context of each call to extend
  function __() { this.constructor = child; }
  child.prototype = 
    parent === null ? 
    // objects instantiated with the child constructor function will
    // inherit from an object that inherits from nothing, not even
    // the built-in JavaScript Object
    Object.create(parent) : 
    (
      // assign the prototype property of the parent constructor function
      // to the prototype property of the constructor function defined
      // above
      __.prototype = parent.prototype, 
      // create the object that all instances of the child will inherit
      // from, and assign it to the prototype property of the child
      // constructor function
      new __()
    );
};

Let's look at the example of using class as interface
Typescript:

// Inheritance
class Point3D extends Point {
  constructor(x: number, y: number, public z: number = 0) {
    super(x, y); // Explicit call to the super class constructor is mandatory
  }

  // Overwrite
  dist() {
    let d = super.dist();
    return Math.sqrt(d * d + this.z * this.z);
  }
}

Javascript:

// Inheritance
var Point3D = /** @class */ (function (_super) {
    __extends(Point3D, _super);
    function Point3D(x, y, z) {
        if (z === void 0) { z = 0; }
        var _this = _super.call(this, x, y) || this;
        _this.z = z;
        return _this;
    }
    // Overwrite
    Point3D.prototype.dist = function () {
        var d = _super.prototype.dist.call(this);
        return Math.sqrt(d * d + this.z * this.z);
    };
    return Point3D;
}(Point));

Modules

Starting with ECMAScript 2015, JavaScript has a concept of modules. TypeScript shares this concept.
Modules are executed within their own scope, not in the global scope; this means that variables, functions, classes, etc. declared in a module are not visible outside the module unless they are explicitly exported using one of the export forms. Conversely, to consume a variable, function, class, interface, etc. exported from a different module, it has to be imported using one of the import forms.

Typescript:

// Modules, "." can be used as the separator for submodules
module Geometry {
  export class Square {
    constructor(public sideLength: number = 0) {
    }
    area() {
      return Math.pow(this.sideLength, 2);
    }
  }
}

let s1 = new Geometry.Square(5);

// Local alias for referencing a module
import G = Geometry;

let s2 = new G.Square(10);

Javascript:

// Modules, "." can be used as separator for sub modules
var Geometry;
(function (Geometry) {
    var Square = /** @class */ (function () {
        function Square(sideLength) {
            if (sideLength === void 0) { sideLength = 0; }
            this.sideLength = sideLength;
        }
        Square.prototype.area = function () {
            return Math.pow(this.sideLength, 2);
        };
        return Square;
    }());
    Geometry.Square = Square;
})(Geometry || (Geometry = {}));
var s1 = new Geometry.Square(5);
// Local alias for referencing a module
var G = Geometry;
var s2 = new G.Square(10);

Generics

A significant part of software engineering is building components that not only have well-defined and consistent APIs but are also reusable.
In languages like C# and Java, one of the primary tools in the toolbox for creating reusable components is generics, that is, being able to create an element that can work over a variety of types rather than a single one. This allows users to consume these components and use their own types.

Typescript:

// Generics
// Classes
class Tuple<T1, T2> {
  constructor(public item1: T1, public item2: T2) {
  }
}

// Interfaces
interface Pair<T> {
  item1: T;
  item2: T;
}

// And functions
let pairToTuple = function <T>(p: Pair<T>) {
  return new Tuple(p.item1, p.item2);
};

let tuple = pairToTuple({ item1: "hello", item2: "world" });

Javascript:

// Generics
// Classes
var Tuple = /** @class */ (function () {
    function Tuple(item1, item2) {
        this.item1 = item1;
        this.item2 = item2;
    }
    return Tuple;
}());
// And functions
var pairToTuple = function (p) {
    return new Tuple(p.item1, p.item2);
};
var tuple = pairToTuple({ item1: "hello", item2: "world" });

References

TypeScript Language Specification
Where X=TypeScript
Typescript Handbook
JavaScript Object Prototypes
JavaScript ES6 Classes and Prototype Inheritance

Hunger Games Guide to the Typescript
Share this

Subscribe to Coding Today

Popular queries