JavaScript ES6

& Typescript

We expect cooperation from all participants to help ensure a safe environment for everybody.

We treat everyone with respect, we refrain from using offensive language and imagery, and we encourage to report any derogatory or offensive behavior to a member of the JSLeague community.

We provide a fantastic environment for everyone to learn and share skills regardless of gender, gender identity and expression, age, sexual orientation, disability, physical appearance, body size, race, ethnicity, religion (or lack thereof), or technology choices.

We value your attendance and your participation in the JSLeague community and expect everyone to accord to the community Code of Conduct at all JSLeague workshops and other events.

Code of conduct

Whoami

Alexandru Albu

Trainer @JSLeague

frontend engineer @10yo

design: photoshop, illustrator

development: javascript. python, sql, mongo

devops: docker

and gaming and basketball

Overview

JavaScript ES6 & Typescript

JavaScript ES6 & Typescript

Agenda

// ES6 

Setting up ES6

Block scope variables

Template strings

Enhanced Objects

Symbols

Arrow functions

Classes

Set && Map

Destructuring

Spread and rest

ES Modules

Promises

Iterators && for loop

Async / Await

Proxies

JavaScript ES6 & Typescript

Agenda

// Typescript

Overview

Type System

Classes

Interfaces

Union / Intersection types

Enums

Generics

Decorators

Setting up ES6

JavaScript ES6 & Typescript

JavaScript ES6 & Typescript

EcmaScript V Year
ES1 1997
ES2 1998
ES3 1999
ES4 abandoned
ES5 2009
ES5.1 2011

JavaScript ES6 & Typescript

EcmaScript V Year
ES6 2015
ES7 2016
ES8 2017
ES9 2018
ES10 2019
ES11 2020
ES12 2021
ES.NEXT -Infinity

JavaScript ES6 & Typescript

How can I use it?

JavaScript ES6 & Typescript

JavaScript ES6 & Typescript

JavaScript ES6 & Typescript

Cross-browser solution:

Babel

Block scope variables

JavaScript ES6 & Typescript

JavaScript ES6 & Typescript

function getColor(condition) {
  if (condition) {
    var value = "blue";

    // other code
    return value;
  } else {
    return null;
  }
}

ES5 Variable Hoisting

JavaScript ES6 & Typescript

function getColor(condition) {
  var value; // undefined
  
  if (condition) {
    var value = "blue";

    // other code
    return value;
  } else {
    // value = undefined
    
    return null;
  }
  
  // value = undefined
}

ES5 Variable Hoisting

variables are hoisted to the top of the enclosing function

JavaScript ES6 & Typescript

for (var i = 0; i < 10; i++) {
  process(items[i]);
}

console.log(i); // 10

ES5 Variables in Loops

JavaScript ES6 & Typescript

var funcs = [];

for (var i = 0; i < 10; i++) {
    funcs.push(function() { console.log(i); });
}

funcs.forEach(function(func) {
    func(); // 10, 10, 10.... 
});

ES5 Functions in Loops

JavaScript ES6 & Typescript

  • Block scope declarations - variables that are inaccessible outside of a given block scope
  • Block scopes (lexical scopes)
    • Inside of a function
    • Inside of a block ( between { and } )
  • ES6 introduces 2 new type of variable declarations:
    • let
    • const

JavaScript ES6 & Typescript

function getColor(condition) {

  if (condition) {
    let value = "blue";

    // other code

    return value;
  } else {

    // value doesn't exist here - ReferenceError
    return null;
  }
  // value doesn't exist here - ReferenceError
}

LET

ReferenceError is thrown outside of scope

JavaScript ES6 & Typescript

var count = 30;

// Syntax error
let count = 40;

Redeclarating LET

LET cannot be redeclarated in the same scope

JavaScript ES6 & Typescript

var count = 30;

// Does not throw an error
if (condition) {

  let count = 40; // shadows the outer variable inside this scope

  // more code
}

Redeclarating LET

LET can be redeclarated in another scope

JavaScript ES6 & Typescript

// Valid constant
const maxItems = 30;

// Syntax error: missing initialization
const name;

maxItems = 6;      // throws error

var message = "Hello!";
let age = 25;

// Each of these would throw an error.
const message = "Goodbye!";
const age = 30;

CONST

CONST content / reference does not change

JavaScript ES6 & Typescript

const person = {
    name: "Andrei"
};

// works
person.name = "George";

// throws an error - reinitialization
person = {
    name: "George"
};

CONST

CONST prevents modification of binding, not the value

JavaScript ES6 & Typescript

if (condition) {
    console.log(typeof value);  // ReferenceError!
    let value = "blue";
}

console.log(typeof value);     // "undefined"

if (condition) {
    let value = "blue";
}

TDZ (Temporal Dead Zone)

LET and CONST cannot be accessed before declaration

JavaScript ES6 & Typescript

for (let i = 0; i < 10; i++) {
  process(items[i]);
}

// i is not accessible here - throws an error
console.log(i);

var funcs = [];

for (let i = 0; i < 10; i++) {
  funcs.push(function() {
    console.log(i);
  });
}

funcs.forEach(function(func) {
  func();     // 0, 1, 2, 3,...
})

Loops - LET

LET creates a new i each time

JavaScript ES6 & Typescript

var funcs = [];

// throws an error after one iteration
for (const i = 0; i < 10; i++) {
  funcs.push(function() {
    console.log(i);
  });
}

var funcs = [],
    object = {
      a: true,
      b: true,
      c: true
    };

// doesn't cause an error
for (const key in object) {
  funcs.push(function() {
    console.log(key);
  });
}

Loops - CONST

Template strings

JavaScript ES6 & Typescript

JavaScript ES6 & Typescript

New string methods:

  • includes() - check if the given text is found anywhere within the string.
  • startsWith() - check if the given text is found at the beginning of the string
  • endsWith() - check if the given text is found at the end of the string
  • repeat() -  returns a new string containing the original string repeated the specified number of times.

JavaScript ES6 & Typescript

var msg = "Hello world!";

console.log(msg.startsWith("Hello"));       // true
console.log(msg.endsWith("!"));             // true
console.log(msg.includes("o"));             // true

console.log(msg.startsWith("o"));           // false
console.log(msg.endsWith("world!"));        // true
console.log(msg.includes("x"));             // false

console.log(msg.startsWith("o", 4));        // true
console.log(msg.endsWith("o", 8));          // true
console.log(msg.includes("o", 8));          // false

console.log("x".repeat(3));         // "xxx"
console.log("hello".repeat(2));     // "hellohello"
console.log("abc".repeat(4));       // "abcabcabcabc"

JavaScript ES6 & Typescript

Template strings

  • Multiline strings A formal concept of multiline strings.
  • Basic string formatting The ability to substitute parts of the string for values contained in variables.
  • HTML escaping The ability to transform a string such that it is safe to insert into HTML.

JavaScript ES6 & Typescript

let message = `Hello world!`;

console.log(message);               // "Hello world!"
console.log(typeof message);        // "string"
console.log(message.length);        // 12

strings delimited by backquotes (`) instead of quotes

JavaScript ES6 & Typescript

// old syntax bug
var message = "Multiline \   
string";

console.log(message);       // "Multiline string"

var message = "Multiline \n\
string";

console.log(message);       // "Multiline
                            //  string"

var message = [
    "Multiline ",
    "string"
].join("\n");

let message = "Multiline \n" +
    "string";

ES5 Multiline strings

JavaScript ES6 & Typescript

let message = `Multiline
string`;

console.log(message);           // "Multiline
                                //  string"
console.log(message.length);    // 16

Preserve newlines

JavaScript ES6 & Typescript

let message = `Multiline
               string`;

console.log(message);           // "Multiline
                                //                 string"
console.log(message.length);    // 31

Preserve tabs

JavaScript ES6 & Typescript

// ES5
var message = "<table>" +
    "<tr>Jane</tr>" +
    "<tr>Bond&</tr>" +
    "<tr>Lars</tr>" +
    "<tr>Croft</tr>" +
"</table>";

// ES6
let message = `
<table>
    <tr>Jane</tr>
    <tr>Bond&</tr>
    <tr>Lars</tr>
    <tr>Croft</tr>
</table>`;

Better HTML strings

JavaScript ES6 & Typescript

let name = "JSLeague",
    message = `Hello, ${name}.`;

console.log(message);       // "Hello, JSLeague."

let count = 10,
    price = 0.25,
    message = `${count} items cost $${(count * price).toFixed(2)}.`;

console.log(message);       // "10 items cost $2.50."

Easier substitutions

JavaScript ES6 & Typescript

let count = 10,
    price = 0.25,
    message = passthru`${count} items cost $${(count * price).toFixed(2)}.`;

function passthru(literals, ...substitutions) {
  // literals: [ "", " items cost $", "." ]
  // substitutions [ 10, "2.50" ]
  
  // return a string
}

Tagged templates

  • literals - an array containing the literal strings as interpreted by JavaScript
  • substitutions - the interpreted value of each substitution

Enhanced Objects

JavaScript ES6 & Typescript

JavaScript ES6 & Typescript

// ES5
function createPerson(name, age) {
  return {
    name: name,
    age: age
  };
}

// ES6
function createPerson(name, age) {
  return {
    name,
    age
  };
}

Property initializer shorthand 

JavaScript ES6 & Typescript

// ES5
var person = {
    name: "Nicholas",
    sayName: function() {
        console.log(this.name);
    }
};

// ES6
var person = {
    name: "Nicholas",
    sayName() {
        console.log(this.name);
    }
};

Concise Methods

JavaScript ES6 & Typescript

let person = {

    // method
    getGreeting() {
        return "Hello";
    }
};

// not a method
function shareGreeting() {
    return "Hi!";
}

Formal Method Definition

A method is a function that has an internal [[HomeObject]] property containing the object to which the method belongs

JavaScript ES6 & Typescript

let person = {
    getGreeting() {
        return "Hello";
    }
};

// prototype is person
let friend = {
    getGreeting() {
        return super.getGreeting() + ", hi!";
    }
};

Object.setPrototypeOf(friend, person);

console.log(friend.getGreeting());  // "Hello, hi!"

Using super() for prototype calls

JavaScript ES6 & Typescript

// ES5
var person = {},
    lastName = "last name";

person["first name"] = "John";
person[lastName] = "Doe";

console.log(person["first name"]);      // "John"
console.log(person[lastName]);          // "Doe"

// ES6
var lastName = "last name";

var person = {
    "first name": "John",
    [lastName]: "Doe"
};

console.log(person["first name"]);      // "John"
console.log(person["last name"]);       // "Doe"

Computed property names

JavaScript ES6 & Typescript

// ES5
var suffix = " name";
var person = {};

person["first" + suffix] = "John";

console.log(person["first name"]);      // "John"

// ES6
var suffix = " name";

var person = {
    ["first" + suffix]: "John",
    ["last" + suffix]: "Doe"
};

console.log(person["first name"]);      // "John"
console.log(person["last name"]);       // "Doe"

Computed property names

JavaScript ES6 & Typescript

console.log(+0 == -0);              // true
console.log(+0 === -0);             // true
console.log(Object.is(+0, -0));     // false

console.log(NaN == NaN);            // false
console.log(NaN === NaN);           // false
console.log(Object.is(NaN, NaN));   // true

console.log(5 == 5);                // true
console.log(5 == "5");              // true
console.log(5 === 5);               // true
console.log(5 === "5");             // false
console.log(Object.is(5, 5));       // true
console.log(Object.is(5, "5"));     // false

New methods - Object.is()

fix the remaining quirks of the identically equals operator

JavaScript ES6 & Typescript

var o1 = { a: 1 };
var o2 = { b: 2 };
var o3 = { c: 3 };

var obj = Object.assign(o1, o2, o3);
console.log(obj); // { a: 1, b: 2, c: 3 }
console.log(o1);  // { a: 1, b: 2, c: 3 }, target object itself is changed.

var safeObj = Object.assign({}, o1, o2, o3);
console.log(safeObj); // { a: 1, b: 2, c: 3 }
console.log(o1);  // { a: 1 }, o1 is the same.

var cloneObj = Object.assign({}, o1);
console.log(cloneObj); // { a: 1 }

New methods - Object.assign()

copy the values of all enumerable own properties from one or more source objects to a target object

Symbols

JavaScript ES6 & Typescript

JavaScript ES6 & Typescript

new JavaScript Object

unique identifier

JavaScript ES6 & Typescript

let id = Symbol();

// or with description

let id = Symbol("id");

JavaScript ES6 & Typescript

Symbols are unique?

let id1 = Symbol("id");
let id2 = Symbol("id");

alert(id1 == id2); // false

JavaScript ES6 & Typescript

Symbols are not strings

let id = Symbol("id");
alert(id); // TypeError: Cannot convert a Symbol value to a string

// insetad do:

let id = Symbol("id");
alert(id.toString()); // Symbol(id), now it works

// or

let id = Symbol("id");
alert(id.description); // id

JavaScript ES6 & Typescript

Symbol as Object key

let idKey = Symbol("id");

let user = {
  name: 'John'
};

// enhance object with new properties without conflicts

user[idKey] = 'some value';

console.log(user[idKey]); // some value

JavaScript ES6 & Typescript

Symbol as Object key are not used in for...in

let idKey = Symbol("id");

let user = {
  name: 'John'
};

// enhance object with new properties without conflicts

user[idKey] = 'some value';

for(let key in user) {
  console.log(key); // only 'name' wil be printed
}

JavaScript ES6 & Typescript

Global Symbols

// read from the global registry
let id = Symbol.for("id"); // if the symbol did not exist, it is created

// read it again (maybe from another part of the code)
let idAgain = Symbol.for("id");

// the same symbol
alert( id === idAgain ); // true

JavaScript ES6 & Typescript

Iterators use Symbols internally

for(let value of user) {...}

// under the hood what this does is:

const user = {
  *[Symbol.iterator]() {
    yield user[key1];
    yield user[key2];
    yield user[key3];
  }
}

Arrow Functions

JavaScript ES6 & Typescript

JavaScript ES6 & Typescript

  • The value of this, super, arguments, and new.target inside of the function is by the closest containing non-arrow function.
  • Functions defined with a new syntax that uses an “arrow” (=>)
  • Difference from "classic" functions
    • Cannot be called with new -  no new.target binding
    • No prototype - no super binding
    • Can’t change this
    • No arguments object
    • No duplicate named parameters

JavaScript ES6 & Typescript

// ES5 function
var reflect = function(value) {
    return value;
};

// ES6 expanded
const reflect = (value) => { return value };

// ES6 short
const reflect = value => value;
  • no parenthesis if only one argument
  • no block (brackets) needed if only return statement

JavaScript ES6 & Typescript

// ES5 equivalent function
var sum = function(num1, num2) {
    return num1 + num2;
};

// ES6
const sum = (num1, num2) => num1 + num2;

Parenthesis needed for 2 or more arguments

JavaScript ES6 & Typescript

// ES5 equivalent function
var getName = function() {
    return "John";
};

// ES6
const getName = () => "John";

Empty parenthesis needed if no arguments

JavaScript ES6 & Typescript

// ES5 equivalent function
var getTempItem = function(id) {
    return {
        id: id,
        name: "Temp"
    };
};

// ES6
const getTempItem = id => ({ id: id, name: "Temp" });

When returning an object, it's necessary to wrap it in ()

JavaScript ES6 & Typescript

var PageHandler = {

    id: "123456",

    init: function() {
        document.addEventListener("click", function(event) {
            this.doSomething(event.type);
        }, false);
    },

    doSomething: function(type) {
        console.log("Handling " + type  + " for " + this.id);
    }
};

PageHandler.init();

event listeners usually bind this to the target of the event

JavaScript ES6 & Typescript

var PageHandler = {

    id: "123456",

    init: function() {
        document.addEventListener("click", (function(event) {
            this.doSomething(event.type);     // no error
        }).bind(this), false);
    },

    doSomething: function(type) {
        console.log("Handling " + type  + " for " + this.id);
    }
};

PageHandler.init();

we can change the this binding (among other ways) by using the .bind() method

JavaScript ES6 & Typescript

function UiComponent {
    var that = this;
    var button = document.getElementById(#myButton);
    button.addEventListener(click,
        function () {
            console.log(CLICK);
            that.handleClick();
        });
    }

UiComponent.prototype.handleClick = function () { ... };

we can also keep a reference to this

JavaScript ES6 & Typescript

var PageHandler = {

    id: "123456",

    init: function() {
        document.addEventListener("click",
                event => this.doSomething(event.type), false);
    },

    doSomething: function(type) {
        console.log("Handling " + type  + " for " + this.id);
    }
};

arrow functions don't bind to this - the value of this inside an arrow function can only be determined by looking up the scope chain

JavaScript ES6 & Typescript

var arr = [5, 6, 13, 0, 1, 18, 23];

// ES5
var sum = arr.reduce(function (a, b) {
    return a + b;
});  // 66

var even = arr.filter(function (v) { 
    return v % 2 == 0;
} // [6, 0, 18]

var double = arr.map(function (v){
    return v * 2;
)}       // [10, 12, 26, 0, 2, 36, 46]

// ES6
var sum = arr.reduce((a, b) => a + b);  // 66

var even = arr.filter(v => v % 2 == 0); // [6, 0, 18]

var double = arr.map(v => v * 2);       // [10, 12, 26, 0, 2, 36, 46]

arrow functions simplify array methods

Classes

JavaScript ES6 & Typescript

JavaScript ES6 & Typescript

function Person(name) {
    this.name = name;
}

Person.prototype.sayName = function() {
    console.log(this.name);
};

let person = new Person("Andrei");
person.sayName();   // outputs "Andrei"

console.log(person instanceof Person);      // true
console.log(person instanceof Object);      // true
console.log(typeof Person);                 // "function"

ES5 "Class"

JavaScript ES6 & Typescript

JavaScript ES6 & Typescript

class PersonClass {

    constructor(name) {     // same as Person constructor function
        this.name = name;
    }

    sayName() {             // same as Person.prototype.sayName
        console.log(this.name);
    }
}

let person = new PersonClass("Andrei");
person.sayName();   // outputs "Andrei"

console.log(person instanceof PersonClass);     // true
console.log(person instanceof Object);          // true

console.log(typeof PersonClass);                    // "function"
console.log(typeof PersonClass.prototype.sayName);  // "function"

ES6 Class

JavaScript ES6 & Typescript

Differences between classes and functions

  • Class declarations, unlike function declarations, are not hoisted
  • All code inside of class declarations runs in strict mode automatically
  • All methods are non-enumerable
  • All methods will throw an error if you try to call them with new
  • Calling the class constructor without new throws an error
  • Attempting to overwrite the class name within a class method throws an error

JavaScript ES6 & Typescript

let PersonClass = class {

    // equivalent of the PersonType constructor
    constructor(name) {
        this.name = name;
    }

    // equivalent of PersonType.prototype.sayName
    sayName() {
        console.log(this.name);
    }
};

let person = new PersonClass("Andrei");
person.sayName();                               // "Andrei"

console.log(person instanceof PersonClass);     // true
console.log(person instanceof Object);          // true

console.log(typeof PersonClass);                    // "function"
console.log(typeof PersonClass.prototype.sayName);  // "function"

Anonymous classes

JavaScript ES6 & Typescript

let PersonClass = class PersonClass2 {

    // equivalent of the PersonType constructor
    constructor(name) {
        this.name = name;
    }

    // equivalent of PersonType.prototype.sayName
    sayName() {
        console.log(this.name);
    }
};

console.log(typeof PersonClass);        // "function"
console.log(typeof PersonClass2);       // "undefined"

Named class expressions

JavaScript ES6 & Typescript

// ES5
let Person = (function() {
    "use strict";

    const Person = function(name) {
        if (typeof new.target === "undefined") {
            throw new Error("Constructor must be called with new.");
        }
        this.name = name;
    }

    Object.defineProperty(CustomHTMLElement.prototype, "name", {
        enumerable: false,
        configurable: true,
        get: function() {
            return this.name;
        },
        set: function(value) {
            this.name= value;
        }
    });

    return Person;
}());

Accessor properties

JavaScript ES6 & Typescript

// ES6
class Person {

    constructor(name) {
        this.name = name;
    }

    get name() {
        return this.name;
    }

    set name(value) {
        this.name = value;
    }
}

var descriptor = Object.getOwnPropertyDescriptor(Person.prototype, "name");
console.log("get" in descriptor);   // true
console.log("set" in descriptor);   // true
console.log(descriptor.enumerable); // false

Accessor properties

JavaScript ES6 & Typescript

// ES5
function PersonType(name) {
    this.name = name;
}

// static method
PersonType.create = function(name) {
    return new PersonType(name);
};

// instance method
PersonType.prototype.sayName = function() {
    console.log(this.name);
};

var person = PersonType.create("Andrei");

Static methods

JavaScript ES6 & Typescript

// ES6
class PersonClass {
    
    constructor(name) { // same as PersonType constructor
        this.name = name;
    }
    
    sayName() { // same as PersonType.prototype.sayName
        console.log(this.name);
    }

    // equivalent of PersonType.create
    static create(name) {
        return new PersonClass(name);
    }
}

let person = PersonClass.create("Andrei");

Static methods

JavaScript ES6 & Typescript

// ES5
function Rectangle(length, width) {
    this.length = length;
    this.width = width;
}

Rectangle.prototype.getArea = function() {
    return this.length * this.width;
};

function Square(length) {
    Rectangle.call(this, length, length);
}

Square.prototype = Object.create(Rectangle.prototype, {
    constructor: {
        value:Square,
        enumerable: true,
        writable: true,
        configurable: true
    }
});


var square = new Square(3);

console.log(square.getArea());              // 9
console.log(square instanceof Square);      // true
console.log(square instanceof Rectangle);   // true

Inheritance

JavaScript ES6 & Typescript

// ES6
class Rectangle {
    constructor(length, width) {
        this.length = length;
        this.width = width;
    }

    getArea() {
        return this.length * this.width;
    }
}

class Square extends Rectangle {
    constructor(length) {
        super(length, length);  // same as Rectangle.call(this, length, length)
    }
}

var square = new Square(3);

console.log(square.getArea());              // 9
console.log(square instanceof Square);      // true
console.log(square instanceof Rectangle);   // true

Inheritance

Derived classes require you to use super() if you specify a constructor

JavaScript ES6 & Typescript

// ES6
class Square extends Rectangle {
    constructor(length) {
        super(length, length);
    }

    // override and shadow Rectangle.prototype.getArea()
    getArea() {
        return this.length * this.length;
    }
}

class Square extends Rectangle {
    constructor(length) {
        super(length, length);
    }

    // override, shadow, and call Rectangle.prototype.getArea()
    getArea() {
        return super.getArea();
    }
}

Shadowing methods

You can call the base class version of the method by using the super

JavaScript ES6 & Typescript

// abstract base class
class Shape {
    constructor() {
        if (new.target === Shape) {
            throw new Error("This class cannot be instantiated directly.")
        }
    }
}

class Rectangle extends Shape {
    constructor(length, width) {
        super();
        this.length = length;
        this.width = width;
    }
}

var x = new Shape();                // throws error

var y = new Rectangle(3, 4);        // no error
console.log(y instanceof Shape);    // true

Abstract classes

Set && Map

JavaScript ES6 & Typescript

JavaScript ES6 & Typescript

A collection of keyed data items, just like an Object.

 

Main difference is that Map allows keys of any type.

Map

JavaScript ES6 & Typescript

Method Effect
new Map() creates the map
map.set(key, value) stores the value by the key
map.get(key) returns the value by the key, undefined if key doesn’t exist in map
map.has(key) returns true if the key exists, false otherwise
map.delete(key) removes the value by the key
map.clear() removes everything from the map
map.size returns the current element count

JavaScript ES6 & Typescript

let map = new Map();

map.set('1', 'str1');   // a string key
map.set(1, 'num1');     // a numeric key
map.set(true, 'bool1'); // a boolean key

// remember the regular Object? it would convert keys to string
// Map keeps the type, so these two are different:
console.log( map.get(1)   ); // 'num1'
console.log( map.get('1') ); // 'str1'

console.log( map.size ); // 3

JavaScript ES6 & Typescript

let recipeMap = new Map([
  ['cucumber', 500],
  ['tomatoes', 350],
  ['onion',    50]
]);

// iterate over keys (vegetables)
for (let vegetable of recipeMap.keys()) {
  console.log(vegetable); // cucumber, tomatoes, onion
}

// iterate over values (amounts)
for (let amount of recipeMap.values()) {
  console.log(amount); // 500, 350, 50
}

// iterate over [key, value] entries
for (let entry of recipeMap) { // the same as of recipeMap.entries()
  console.log(entry); // cucumber,500 (and so on)
}

JavaScript ES6 & Typescript

Map from datasets

// array of [key, value] pairs
let map = new Map([
  ['1',  'str1'],
  [1,    'num1'],
  [true, 'bool1']
]);

console.log( map.get('1') ); // str1

let obj = {
  name: "John",
  age: 30
};

let map = new Map(Object.entries(obj));

console.log( map.get('name') ); // John

JavaScript ES6 & Typescript

Objects from Map

let map = new Map();
map.set('banana', 1);
map.set('orange', 2);
map.set('meat', 4);

let obj = Object.fromEntries(map.entries()); // make a plain object (*)

// done!
// obj = { banana: 1, orange: 2, meat: 4 }

console.log(obj.orange); // 2

// or shorter:

let obj = Object.fromEntries(map); // omit .entries()

JavaScript ES6 & Typescript

Special type collection – “set of values” (without keys), where each value may occur only once.

Set

JavaScript ES6 & Typescript

Method Effect
new Set(iterable) creates the set, and if an iterable object is provided (usually an array), copies values from it into the set
set.add(value) adds a value, returns the set itself
set.delete(value) removes the value, returns true if value existed at the moment of the call, otherwise false
set.has(value) returns true if the value exists in the set, otherwise false
set.clear() removes everything from the set
set.size is the elements count

JavaScript ES6 & Typescript

let set = new Set();

let john = { name: "John" };
let pete = { name: "Pete" };
let mary = { name: "Mary" };

// visits, some users come multiple times
set.add(john);
set.add(pete);
set.add(mary);
set.add(john);
set.add(mary);

// set keeps only unique values
console.log( set.size ); // 3

for (let user of set) {
  console.log(user.name); // John (then Pete and Mary)
}

JavaScript ES6 & Typescript

let set = new Set(["oranges", "apples", "bananas"]);

for (let value of set) console.log(value);

// the same with forEach:
set.forEach((value, valueAgain, set) => {
  console.log(value);
});

Destructuring

JavaScript ES6 & Typescript

JavaScript ES6 & Typescript

the process of breaking a data structure down into smaller parts

JavaScript ES6 & Typescript

let toDoItem = {
    name: "Walk dog",
    isDone: false
};

// ES5
// extract data from the object
let name = toDoItem.name;
let isDone = toDoItem.isDone;

// ES6
let { name, isDone } = toDoItem;

console.log(name);      // "Walk dog"
console.log(isDone);      // false

JavaScript ES6 & Typescript

// safe init
let toDoItem = {
    name: "Walk dog",
    isDone: false
};

let { name, isDone, tag } = toDoItem;

console.log(name);        // "Walk dog"
console.log(isDone);      // false
console.log(tag);         // undefined

// default values
let toDoItem = {
    name: "Walk dog",
    isDone: false
};

let { name, isDone, tag = "Home" } = toDoItem;

console.log(name);        // "Walk dog"
console.log(isDone);      // false
console.log(tag);         // "Home"

Default values

JavaScript ES6 & Typescript

// renaming
let toDoItem = {
    name: "Walk dog",
    isDone: false
};

let { name: toDoName, isDone: doneState } = toDoItem;

console.log(toDoName);        // "Walk dog"
console.log(doneState);       // false

// with default values
let toDoItem = {
    name: "Walk dog"
};

let { name: toDoName, isDone: doneState = false } = toDoItem;

console.log(toDoName);        // "Walk dog"
console.log(doneState);       // false

Renaming variables

JavaScript ES6 & Typescript

let toDoItem = {
    details: {
        name: "Walk dog",
        dueDate: "11.03.2017"
    },
    isDone: false
};

// extract toDoItem.details.name
let { details: { name: toDoName } } = toDoItem;

console.log(toDoName)    // "Walk dog"

Multilevel

JavaScript ES6 & Typescript

// Array
let colors = [ "red", "green", "blue" ];

let [ firstColor, secondColor ] = colors;

console.log(firstColor);        // "red"
console.log(secondColor);       // "green"

// Nested
let colors = [ "red", [ "green", "lightgreen" ], "blue" ];

let [ firstColor, [ secondColor ] ] = colors;

console.log(firstColor);        // "red"
console.log(secondColor);       // "green"

// Skip values
let colors = [ "red", "green", "blue" ];

let [ , , thirdColor ] = colors;

console.log(thirdColor);        // "blue"

// Swapping
let a = 1,
    b = 2;

[ a, b ] = [ b, a ];

console.log(a);     // 2
console.log(b);     // 1

Array destructuring

JavaScript ES6 & Typescript

let mixed = {
  one: 1,
  two: 2,
  values: [3, 4, 5]
};
let { one: a, two: b, values: [c, , e] } = mixed;

console.log(a); // 1
console.log(b); // 2
console.log(c); // 3
console.log(e); // 5

Mixed destructuring

JavaScript ES6 & Typescript

// Wihout destructuring
const animal = {
  name: 'Dog',
  sound: 'wof'
};

function makeSound(options) {
  options.name = options.name || 'animal';
  console.log(`The ${options.animal} goes ${options.sound}`)
}

makeSound(animal);

// With destructuring
const animal = {
  name: 'Dog',
  sound: 'wof'
};

function makeSound({name = 'animal', sound}) {
   console.log(`The ${name} goes ${sound}`)
}

makeSound(animal);

Destructured function parameters

JavaScript ES6 & Typescript

function setCookie(name, value,
    {
        secure = false,
        path = "/",
        domain = "example.com",
        expires = new Date(Date.now() + 360000000)
    } = {}
) {

    // ...
}

Spread and Rest

JavaScript ES6 & Typescript

JavaScript ES6 & Typescript

function logNumbers(a, b, c) {
  console.log(a)
  console.log(b)
  console.log(c)
}

const array = [1,2,3];

logNumbers(...array); // 1 2 3

Spread operator

the spread operator allows you to specify an array that should be split and have its items passed in as separate arguments to a function

JavaScript ES6 & Typescript

// Array
const array = [1,2,3];

console.log([...array, 4, 5]);  // [1, 2, 3, 4, 5]

// Set
let set = new Set([1, 2, 3, 3, 3, 4, 5]),
    array = [...set];

console.log(array);             // [1,2,3,4,5]

// Object
var obj = {'key1': 'value1'};
var array = [...obj]; // TypeError: obj is not iterable

Structures

JavaScript ES6 & Typescript

// Concatenation
const arr1 = [1,2,3];
const arr2 = [2,3,4];

const arr3 = [...arr1, 8, ...arr2];

console.log(arr3); // [1, 2, 3, 8, 2, 3, 4]

// Copy
const arr1 = [1,2,3];
const arr2 = [...arr1]

console.log(arr1); // [1, 2, 3]
console.log(arr2); // [1, 2, 3]
console.log(arr1 === arr2); // false

Array

JavaScript ES6 & Typescript

// Concatenation
const obj1 = {a: 'a', b: 'b'};
const obj2 = {c: 'c', ...obj1};

console.log(obj2);    // {a: 'a', b: 'b', c: 'c'}

// Overwrite
const obj1 = {a: 'a', b: 'b', c: 'c'};
const obj2 = {...obj1, c: 'd'};

console.log(obj2); // {a: 'a', b: 'b', c: 'd'}

Object

JavaScript ES6 & Typescript

function pick(object) {
    let result = Object.create(null);

    // start at the second parameter
    for (let i = 1, len = arguments.length; i < len; i++) {
        result[arguments[i]] = object[arguments[i]];
    }

    return result;
}

let book = {
    title: "Understanding ECMAScript 6",
    author: "Nicholas C. Zakas",
    year: 2015
};

let bookData = pick(book, "author", "year");

console.log(bookData.author);   // "Nicholas C. Zakas"
console.log(bookData.year);     // 2015

Unnamed parameters in ES5

JavaScript ES6 & Typescript

function pick(object, ...keys) {
    let result = Object.create(null);

    for (let i = 0, len = keys.length; i < len; i++) {
        result[keys[i]] = object[keys[i]];
    }

    return result;
}

Rest parameters in ES6

The rest parameter becomes an Array containing the rest of the parameters passed to the function

JavaScript ES6 & Typescript

function pick(object, ...keys, last) {
    let result = Object.create(null);

    for (let i = 0, len = keys.length; i < len; i++) {
        result[keys[i]] = object[keys[i]];
    }

    return result;
}

Restrictions

Can't have a named parameter after rest parameters

JavaScript ES6 & Typescript

let object = {

    set name(...value) {
        // do something
    }
};

Restrictions

Can't use rest parameter in setter

JavaScript ES6 & Typescript

let { x, y, ...z } = { x: 1, y: 2, a: 3, b: 4 };

console.log(x); // 1
console.log(y); // 2
console.log(z); // { a: 3, b: 4 }

Objects

Rest properties collect the remaining own enumerable property keys that are not already picked off by the destructuring pattern

Modules

JavaScript ES6 & Typescript

JavaScript ES6 & Typescript

Modules

- ES6 way to solve the "everything is in one scope" problem

  • Module code automatically runs in strict mode
  • Variables created in the top level of a module aren’t automatically added to the shared global scope.
  • The value of this in the top level of a module is undefined.
  • Modules must export anything that should be available to code outside of the module.
  • Modules may import bindings from other modules.

JavaScript ES6 & Typescript

Export

// Data
export var color = "red";
export let person = { name : "Andrei" };
export const value = 7;

// Functions
export function sum(num1, num2) {
    return num1 + num1;
}

// Classes
export class Rectangle {
    constructor(length, width) {
        this.length = length;
        this.width = width;
    }
}

JavaScript ES6 & Typescript

Export references

// this function is private to the module
function subtract(num1, num2) {
    return num1 - num2;
}

function multiply(num1, num2) {
    return num1 * num2;
}

export { multiply };

JavaScript ES6 & Typescript

Import

// Single binding
import { sum } from "./example.js";

console.log(sum(1, 2));     // 3

sum = 1;        // error

// Multiple bindings
import { sum, multiply, number } from "./example.js";

console.log(sum(1, number));   // 8
console.log(multiply(1, 2));        // 2

// All module
import * as example from "./example.js";

console.log(example.sum(1, example.number));          // 8
console.log(example.multiply(1, 2));    // 2

JavaScript ES6 & Typescript

Import

// module gets executed only once
import { sum } from "./example.js";
import { multiply } from "./example.js";
import { number } from "./example.js";

module gets executed only once

JavaScript ES6 & Typescript

Import

if (flag) {
    export flag;    // syntax error
}

function tryImport() {
    import flag from "./example.js";    // syntax error
}

imports / exports cannot be done in statements

JavaScript ES6 & Typescript

Import / Export

// source.example.js
export var name = "John";
export function setName(newName) {
    name = newName;
}

// dest.example.js
import { name, setName } from "./source.example.js";

console.log(name);       // "John"
setName("Jane");
console.log(name);       // "Jane"

name = "Joe";       // error

cannot modify import bindings

JavaScript ES6 & Typescript

Import / Export

// exports
function sum(num1, num2) {
    return num1 + num2;
}

export { sum as add };

// imports
import { add as sum } from "./example.js";

console.log(typeof add);            // "undefined"
console.log(sum(1, 2));             // 3

imports / exports can be renamed

JavaScript ES6 & Typescript

Import / Export

// option 1
// export
export default function(num1, num2) {
    return num1 + num2;
}

// import
import sum from "./example.js";

console.log(sum(1, 2));     // 3

// option 2
// export
function sum(num1, num2) {
    return num1 + num2;
}

export { sum as default };

// import
import sum from "./example.js";

console.log(sum(1, 2));     // 3

// option 3
// export
export let color = "red";

export default function(num1, num2) {
    return num1 + num2;
}

// import
import sum, { color } from "./example.js";

console.log(sum(1, 2));     // 3
console.log(color);         // "red"

you can export / import as default

JavaScript ES6 & Typescript

Import / Export

// export
// example.js
Array.prototype.pushAll = function(items) {

    // items must be an array
    if (!Array.isArray(items)) {
        throw new TypeError("Argument must be an array.");
    }

    // use built-in push() and spread operator
    return this.push(...items);
};

// import
import "./example.js";

let colors = ["red", "green", "blue"];
let items = [];

items.pushAll(colors);

you can import without use of bindings

JavaScript ES6 & Typescript

Import / Export

<!-- load a module JavaScript file -->
<script type="module" src="module.js"></script>

<!-- include a module inline -->
<script type="module">

import { sum } from "./example.js";

let result = sum(1, 2);

</script>

importing in web browsers

Promises

JavaScript ES6 & Typescript

JavaScript ES6 & Typescript

Function that doesn't block working thread and allows code to be resolved at a later moment in the execution chain.

JavaScript ES6 & Typescript

let promise = new Promise(function(resolve, reject) {
  // executor
});

Empty promise

JavaScript ES6 & Typescript

let promise = new Promise(function(resolve, reject) {
  resolve("done");

  reject(new Error("…")); // ignored
  setTimeout(() => resolve("…")); // ignored
});

Executor thread

JavaScript ES6 & Typescript

let promise = new Promise(function(resolve, reject) {
  setTimeout(() => resolve("done!"), 1000);
});

// resolve runs the first function in .then
promise.then(
  result => conosole.log(result), // shows "done!" after 1 second
  error => conosole.log(error) // doesn't run
);

// short equivalent
promise.then(result => conosole.log(result));

Successful Promise

JavaScript ES6 & Typescript

let promise = new Promise(function(resolve, reject) {
  setTimeout(() => reject(new Error("Whoops!")), 1000);
});

// reject runs the second function in .then
promise.then(
  result => alert(result), // doesn't run
  error => alert(error) // shows "Error: Whoops!" after 1 second
);

// short equivalent
promise.catch(error => conosole.log(error));

Failed Promise

JavaScript ES6 & Typescript

let promise = new Promise(function(resolve, reject) {
  setTimeout(() => reject(new Error("Whoops!")), 1000);
});

// finally always runs no matter resolution or rejection
promise
  .catch(error => conosole.log(error))
  .finally(() => console.log('Promise done!'));

Ultimate resolution

JavaScript ES6 & Typescript

let promise = new Promise(function(resolve, reject) {
  setTimeout(() => resolve("first promise done!"), 1000);
});

promise
  .then(result => {
    return new Promise(function(resolve, reject) {
      setTimeout(() => resolve("second promise done!"), 1000);
    });
  })
  .then(result => {
  	console.log(result) // shows "second promise done!"
  })
  .catch(error => console.log(error));

Promise chains

Iterators

JavaScript ES6 & Typescript

JavaScript ES6 & Typescript

var colors = ["red", "green", "blue"];

for (var i = 0, len = colors.length; i < len; i++) {
    console.log(colors[i]);
}

For loop

Tend to get complicated when complex logic and nesting is required

JavaScript ES6 & Typescript

function createIterator(items) {
    var i = 0;

    return {
        next: function() {

            var done = (i >= items.length);
            var value = !done ? items[i++] : undefined;

            return {
                done: done,
                value: value
            };

        }
    };
}

var iterator = createIterator([1, 2, 3]);

console.log(iterator.next());           // "{ value: 1, done: false }"
console.log(iterator.next());           // "{ value: 2, done: false }"
console.log(iterator.next());           // "{ value: 3, done: false }"
console.log(iterator.next());           // "{ value: undefined, done: true }"
// for all further calls
console.log(iterator.next());           // "{ value: undefined, done: true }"

Iterators

JavaScript ES6 & Typescript

// generator
function *createIterator() {
    yield 1;
    yield 2;
    yield 3;
}

// generators are called like regular functions but return an iterator
let iterator = createIterator();

console.log(iterator.next().value);     // 1
console.log(iterator.next().value);     // 2
console.log(iterator.next().value);     // 3

// for all further calls
console.log(iterator.next().value);     // undefined

Generators

functions that return an iterator

JavaScript ES6 & Typescript

  • The * before the function name makes the function a generator.

Key characteristics

  • The yield keyword specifies values the resulting iterator should return when next() is called

JavaScript ES6 & Typescript

// iterator as a function
let createIterator = function *(items) {
    for (let i = 0; i < items.length; i++) {
        yield items[i];
    }
};

let iterator = createIterator([1, 2, 3]);

// iterator inside object
var o = {
    createIterator: function *(items) {
        for (let i = 0; i < items.length; i++) {
            yield items[i];
        }
    }
};

let iterator = o.createIterator([1, 2, 3]);

Creating an arrow function that is also a generator is not possible.

Generators

JavaScript ES6 & Typescript

// example 1
let values = [1, 2, 3];

for (let num of values) {
    console.log(num);
}

//   1
//   2
//   3

// example 2
let divs = document.getElementsByTagName("div"); // NodeList Iterator

for (let div of divs) {
    console.log(div.id);
}

for...of iterator

JavaScript ES6 & Typescript

  • A for-of loop calls next() on an iterable
  • A for-of loop first calls the Symbol.iterator method on the values array to retrieve an iterator.
  • The for-of statement will throw an error when used on, a non-iterable object, null, or undefined.

JavaScript ES6 & Typescript

Accessing the default iterator

let values = [1, 2, 3];
let iterator = values[Symbol.iterator]();

console.log(iterator.next());           // "{ value: 1, done: false }"
console.log(iterator.next());           // "{ value: 2, done: false }"
console.log(iterator.next());           // "{ value: 3, done: false }"
console.log(iterator.next());           // "{ value: undefined, done: true }"

JavaScript ES6 & Typescript

Detecting if an object is iterable

function isIterable(object) {
    return typeof object[Symbol.iterator] === "function";
}

console.log(isIterable([1, 2, 3]));     // true
console.log(isIterable("Hello"));       // true
console.log(isIterable(new Map()));     // true
console.log(isIterable(new Set()));     // true
console.log(isIterable(new WeakMap())); // false
console.log(isIterable(new WeakSet())); // false

JavaScript ES6 & Typescript

Creating iterables

let collection = {
    items: [],
    *[Symbol.iterator]() {
        for (let item of this.items) {
            yield item;
        }
    }

};

collection.items.push(1);
collection.items.push(2);
collection.items.push(3);

for (let x of collection) {
    console.log(x);
}

//  1
//  2
//  3

JavaScript ES6 & Typescript

Passing arguments to iterators

function *createIterator() {
    let first = yield 1;
    let second = yield first + 2;       // 4 + 2
    yield second + 3;                   // 5 + 3
}

let iterator = createIterator();

console.log(iterator.next());           // "{ value: 1, done: false }"
console.log(iterator.next(4));          // "{ value: 6, done: false }"
console.log(iterator.next(5));          // "{ value: 8, done: false }"
console.log(iterator.next());           // "{ value: undefined, done: true }"

JavaScript ES6 & Typescript

Throwing error in iterators

function *createIterator() {
    let first = yield 1;
    let second = yield first + 2;       // yield 4 + 2, then throw
    yield second + 3;                   // never is executed
}

let iterator = createIterator();

console.log(iterator.next());                   // "{ value: 1, done: false }"
console.log(iterator.next(4));                  // "{ value: 6, done: false }"
console.log(iterator.throw(new Error("Boom"))); // error thrown from generator

JavaScript ES6 & Typescript

Generator return value

// return without value
function *createIterator() {
    yield 1;
    return;
    yield 2;
    yield 3;
}

let iterator = createIterator();

console.log(iterator.next());           // "{ value: 1, done: false }"
console.log(iterator.next());           // "{ value: undefined, done: true }"

// return with value
function *createIterator() {
    yield 1;
    return 42;
}

let iterator = createIterator();

console.log(iterator.next());           // "{ value: 1, done: false }"
console.log(iterator.next());           // "{ value: 42, done: true }"
console.log(iterator.next());           // "{ value: undefined, done: true }"

Async / Await

JavaScript ES6 & Typescript

JavaScript ES6 & Typescript

async function getName() {
  const url = 'https://jsonplaceholder.typicode.com/';
  let response = await fetch(`${url}posts/-1`);

  const post = await response.json();
  const userId = post.userId;

  response = await fetch(`${url}users/${userId}`);

  const user = await response.json();

  return user.name;
}

getName()
  .then(name => console.log(name))
  .catch(error => console.log('error'));

// or with await
const name = await getName(); // will throw error because not async context

(async () => {
  const name = await getName(); // now await works!
})();

JavaScript ES6 & Typescript

  • Any function can be async
  • Awaiting async can only be done in async context
  • Async functions return a promise

Proxies

JavaScript ES6 & Typescript

JavaScript ES6 & Typescript

The Proxy object enables you to create a proxy for another object, which can intercept and redefine fundamental operations for that object.

JavaScript ES6 & Typescript

let proxy = new Proxy(target, handler)
  • target – is an object to wrap, can be anything, including functions.
  • handler – proxy configuration: an object with “traps”, methods that intercept operations. – e.g. get trap for reading a property of target, set trap for writing a property into target, and so on.

JavaScript ES6 & Typescript

const target = {
  message1: "hello",
  message2: "everyone"
};

const handler1 = {};

const proxy1 = new Proxy(target, handler1);

console.log(proxy1.message1); // hello
console.log(proxy1.message2); // everyone

JavaScript ES6 & Typescript

const target = {
  message1: "hello",
  message2: "everyone"
};

const handler2 = {
  get: function(target, prop, receiver) {
    return "world";
  }
};

const proxy2 = new Proxy(target, handler2);

console.log(proxy2.message1); // world
console.log(proxy2.message2); // world

JavaScript ES6 & Typescript

Reflect is a built-in object that provides methods for interceptable JavaScript operations. The methods are the same as those of proxy handlers. Reflect is not a function object, so it's not constructible.

JavaScript ES6 & Typescript

const target = {
  message1: "hello",
  message2: "everyone"
};

const handler3 = {
  get: function (target, prop, receiver) {
    if (prop === "message2") {
      return "world";
    }
    return Reflect.get(...arguments);
  },
};

const proxy3 = new Proxy(target, handler3);

console.log(proxy3.message1); // hello
console.log(proxy3.message2); // world

Typescript Overview

JavaScript ES6 & Typescript

JavaScript ES6 & Typescript

  • Superset of ECMAScript
  • Open Source
  • Anders Hejlsberg @ Microsoft, 2012

JavaScript ES6 & Typescript

How does it work?

JavaScript ES6 & Typescript

JavaScript ES6 & Typescript

Using TypeScript

// installing
npm i -g typescript@latest // globally

// or

npm i typescript@latest // local
// using
tsc script.ts // globally

// or

node path/to/tsc/bin script.ts // local

JavaScript ES6 & Typescript

Configuring TypeScript

// tsconfig.json
{
  "compilerOptions": {
    "module": "system",
    "noImplicitAny": true,
    "removeComments": true,
    "preserveConstEnums": true,
    "outFile": "../../built/local/tsc.js",
    "sourceMap": true
  },
  "include": ["src/**/*"],
  "exclude": ["node_modules", "**/*.spec.ts"]
}

Type System

JavaScript ES6 & Typescript

JavaScript ES6 & Typescript

let animal;         // declares only a value
interface Mamal {}  // declares a type
class Dog {}        // declares a value and a type

Values end up in resulting JavaScript, types don't

JavaScript ES6 & Typescript

any Type

let obj: any = { x: 0 };
// None of the following lines of code will throw compiler errors.
// Using `any` disables all further type checking, and it is assumed 
// you know the environment better than TypeScript.
obj.foo();
obj();
obj.bar = 100;
obj = "hello";
const n: number = obj;

noImplicitAny - When you don’t specify a type, and TypeScript can’t infer it from context, the compiler will typically default to any.

You usually want to avoid this, though, because any isn’t type-checked. Use the compiler flag noImplicitAny to flag any implicit any as an error.

JavaScript ES6 & Typescript

primitive Types

  • string - represents string values like "Hello, world"
  • number - is for numbers like 42. JavaScript does not have a special runtime value for integers, so there’s no equivalent to int or float - everything is simply number
  • boolean - is for the two values true and false

JavaScript ES6 & Typescript

primitive Types

let myName: string = "John";
const answerToLife: number = 42;
var isJSWorkshop: boolean = true;

JavaScript ES6 & Typescript

Object Type

// The parameter's type annotation is an object type
function printCoord(pt: { x: number; y: number }) {
  console.log("The coordinate's x value is " + pt.x);
  console.log("The coordinate's y value is " + pt.y);
}
printCoord({ x: 3, y: 7 });

// or 
type Coordonate = { x: number, y: number };

function printCoord(pt: Coordinate) { ... }

JavaScript ES6 & Typescript

Array Type

// without a specific type
var bytes: [] = [true, true, false, false]; // equivalent to any[]
 
// array of primitives
let primes: number[] = [1, 2, 3, 5, 7, 9];
let primes: Array<number> = [1, 2, 3, 5, 7, 9]; // another type notation

// array of objects
const users: {firstName: string, lastName: string}[] = [
  {firstName: 'Bob', lastName: 'Ross'}
];

JavaScript ES6 & Typescript

Types in functions

// Parameter type annotation
function greet(name: string) {
  console.log("Hello, " + name.toUpperCase() + "!!");
}

// Return type
function getFavoriteNumber(): number {
  return 26;
}

JavaScript ES6 & Typescript

Type Aliases

type Point = {
  x: number;
  y: number;
};
 
// Exactly the same as the earlier example
function printCoord(pt: Point) {
  console.log("The coordinate's x value is " + pt.x);
  console.log("The coordinate's y value is " + pt.y);
}
 
printCoord({ x: 100, y: 100 });

JavaScript ES6 & Typescript

Union Types

function printId(id: number | string) {
  console.log("Your ID is: " + id);
}

// OK
printId(101);

// OK
printId("202");

// Error
printId({ myID: 22342 });

JavaScript ES6 & Typescript

Intersection Types

type Point = {
  x: number;
  y: number;
};

type SpacialPoint = Point & { z: number };
 
// Exactly the same as the earlier example
function printCoord(pt: SpacialPoint) {
  console.log("The coordinate's x value is " + pt.x);
  console.log("The coordinate's y value is " + pt.y);
  console.log("The coordinate's z value is " + pt.z);
}
 
printCoord({ x: 100, y: 100, z: 100 });

Interfaces

JavaScript ES6 & Typescript

JavaScript ES6 & Typescript

interface Animal {
  name: string
}

interface Bear extends Animal {
  honey: boolean
}

const bear = getBear() 
bear.name
bear.honey

All types are expressible as interfaces

type Animal = {
  name: string
}

type Bear = Animal & { 
  honey: boolean 
}

const bear = getBear();
bear.name;
bear.honey;

JavaScript ES6 & Typescript

interface Window {
  title: string
}

interface Window {
  ts: TypeScriptAPI
}

const src = 'const a = "Hello World"';
window.ts.transpileModule(src, {});

Interfaces are extendable, types are not

type Window = {
  title: string
}

type Window = {
  ts: TypeScriptAPI
}

 // Error: Duplicate identifier 'Window'.

Enums

JavaScript ES6 & Typescript

JavaScript ES6 & Typescript

Numeric enums

enum Direction {
  Up = 1,
  Down, // Auto-increment to 2
  Left, // Auto-increment to 3
  Right,
}
  
enum Direction {
  Up, // Automatically set to 0
  Down, // Auto-increment to 1
  Left,
  Right,
}

JavaScript ES6 & Typescript

String enums

enum Direction {
  Up = "UP",
  Down = "DOWN",
  Left = "LEFT",
  Right = "RIGHT",
}

JavaScript ES6 & Typescript

Const enums

const enum Direction {
  Up,
  Down,
  Left,
  Right,
}
 
let directions = [
  Direction.Up,
  Direction.Down,
  Direction.Left,
  Direction.Right,
];

// compiles into 
"use strict";
let directions = [
    0 /* Up */,
    1 /* Down */,
    2 /* Left */,
    3 /* Right */,
];

Classes

JavaScript ES6 & Typescript

JavaScript ES6 & Typescript

class Point {}

TypeScript offers full support for the class keyword introduced in ES2015.

As with other JavaScript language features, TypeScript adds type annotations and other syntax to allow you to express relationships between classes and other types.

JavaScript ES6 & Typescript

class Point {
  x: number;
  y: number;
}
 
const pt = new Point();
pt.x = 0;
pt.y = 0;class Point {}

Class fields

JavaScript ES6 & Typescript

class Point {
  x: number = 0;
  y: number = 0;
}
 
const pt = new Point();
// Prints 0, 0
console.log(`${pt.x}, ${pt.y}`);

Class fields initializers

JavaScript ES6 & Typescript

class GoodGreeter {
  name: string;
 
  constructor() {
    this.name = "hello";
  }
}

Class fields initializers

JavaScript ES6 & Typescript

class Greeter {
  readonly name: string = "world";
 
  constructor(otherName?: string) {
    if (otherName !== undefined) {
      this.name = otherName;
    }
  }
 
  err() {
    // Cannot assign to 'name' because it is a read-only property.
    this.name = "not ok";
  }
}

const g = new Greeter();

// Cannot assign to 'name' because it is a read-only property.
g.name = "also not ok";

Read-only fields

JavaScript ES6 & Typescript

class Point {
  x: number;
  y: number;
 
  // Normal signature with defaults
  constructor(x = 0, y = 0) {
    this.x = x;
    this.y = y;
  }
}

Constructors

JavaScript ES6 & Typescript

class Point {
  // Overloads
  constructor(x: number, y: string);
  constructor(s: string);
  constructor(xs: any, y?: any) {
    // TBD
  }
}

Constructors

JavaScript ES6 & Typescript

class Point {
  x = 10;
  y = 10;
 
  scale(n: number): void {
    this.x *= n;
    this.y *= n;
  }
}

Methods

JavaScript ES6 & Typescript

class C {
  _length = 0;
  get length() {
    return this._length;
  }
  set length(value) {
    this._length = value;
  }
}

Accessors

JavaScript ES6 & Typescript

Accessors

TypeScript has some special inference rules for accessors:

  • If get exists but no set, the property is automatically readonly
  • If the type of the setter parameter is not specified, it is inferred from the return type of the getter
  • Getters and setters must have the same member visibility

JavaScript ES6 & Typescript

class Base {
  k = 4;
}
 
class Derived extends Base {
  constructor() {
    // Prints a wrong value in ES5; throws exception in ES6
    console.log(this.k);
	
    // 'super' must be called before accessing 'this' 
    // in the constructor of a derived class.
    super();
  }
}

Class extending

JavaScript ES6 & Typescript

Heritage

interface Pingable {
  ping(): void;
}
 
class Sonar implements Pingable {
  ping() {
    console.log("ping!");
  }
}

// Class 'Ball' incorrectly implements interface 'Pingable'.
// Property 'ping' is missing in type 'Ball' but required in type 'Pingable'.
class Ball implements Pingable {
  pong() {
    console.log("pong!");
  }
}

JavaScript ES6 & Typescript

Overriding Methods

class Base {
  greet() {
    console.log("Hello, world!");
  }
}
 
class Derived extends Base {
  greet(name?: string) {
    if (name === undefined) {
      super.greet();
    } else {
      console.log(`Hello, ${name.toUpperCase()}`);
    }
  }
}
 
const d = new Derived();
d.greet();
d.greet("reader");

JavaScript ES6 & Typescript

Member visibility

// public
class Greeter {
  public greet() {
    console.log("hi!");
  }
}
const g = new Greeter();
g.greet();

The default visibility of class members is public. A public member can be accessed anywhere:

JavaScript ES6 & Typescript

Member visibility

class Greeter {
  public greet() {
    console.log("Hello, " + this.getName());
  }
  protected getName() {
    return "hi";
  }
}
 
class SpecialGreeter extends Greeter {
  public howdy() {
    // OK to access protected member here
    console.log("Howdy, " + this.getName());
  }
}
const g = new SpecialGreeter();
g.greet(); // OK
// Property 'getName' is protected and only accessible 
// within class 'Greeter' and its subclasses.
g.getName(); 

JavaScript ES6 & Typescript

Member visibility

class Base {
  private x = 0;
}
const b = new Base();

// Property 'x' is private and only accessible within class 'Base'.
console.log(b.x);

JavaScript ES6 & Typescript

Static member

class MyClass {
  static x = 0;
  static printX() {
    console.log(MyClass.x);
  }
}

console.log(MyClass.x);

MyClass.printX();

JavaScript ES6 & Typescript

Static member

// Unnecessary "static" class
class MyStaticClass {
  static doSomething() {}
}
 
// Preferred (alternative 1)
function doSomething() {}
 
// Preferred (alternative 2)
const MyHelperObject = {
  dosomething() {},
};

Generics

JavaScript ES6 & Typescript

JavaScript ES6 & Typescript

Components that are capable of working on the data of today as well as the data of tomorrow will give you the most flexible capabilities for building up large software systems.

JavaScript ES6 & Typescript

function identity<Type>(arg: Type): Type {
  return arg;
}

JavaScript ES6 & Typescript

function identity<Type>(arg: Type): Type {
  return arg;
}
 
let myIdentity: <Input>(arg: Input) => Input = identity;

Generic Type

JavaScript ES6 & Typescript

class GenericNumber<NumType> {
  zeroValue: NumType;
  add: (x: NumType, y: NumType) => NumType;
}
 
let myGenericNumber = new GenericNumber<number>();
myGenericNumber.zeroValue = 0;
myGenericNumber.add = function (x, y) {
  return x + y;
};

Generic Class

JavaScript ES6 & Typescript

function loggingIdentity<Type>(arg: Type): Type {
  console.log(arg.length); // Property 'length' does not exist on type 'Type'.
  return arg;
}

// solution
interface Lengthwise {
  length: number;
}
 
function loggingIdentity<Type extends Lengthwise>(arg: Type): Type {
  console.log(arg.length); // Now we know it has a .length property, so no more error
  return arg;
}

Generic Constants

Decorators

JavaScript ES6 & Typescript

JavaScript ES6 & Typescript

A Decorator is a special kind of declaration that can be attached to a class declaration, method, accessor, property, or parameter.

JavaScript ES6 & Typescript

Decorators use the form @expression, where expression must evaluate to a function that will be called at runtime with information about the decorated declaration.

JavaScript ES6 & Typescript

Decorators are an experimental feature in TypeScript so we must enable them in order to use.

JavaScript ES6 & Typescript

function color(value: string) {
  // this is the decorator factory, it sets up
  // the returned decorator function
  return function (target) {
    // this is the decorator
    // do something with 'target' and 'value'...
  };
}

Decorator Factory

JavaScript ES6 & Typescript

function first() {
  console.log("first(): factory evaluated");
  return function (target: any, propertyKey: string, descriptor: PropertyDescriptor) {
    console.log("first(): called");
  };
}
 
function second() {
  console.log("second(): factory evaluated");
  return function (target: any, propertyKey: string, descriptor: PropertyDescriptor) {
    console.log("second(): called");
  };
}
 
class ExampleClass {
  @first()
  @second()
  method() {}
}

Decorator Composition

JavaScript ES6 & Typescript

Decorator Evaluation

  1. Parameter Decorators, followed by Method, Accessor, or Property Decorators are applied for each instance member.
  2. Parameter Decorators, followed by Method, Accessor, or Property Decorators are applied for each static member.
  3. Parameter Decorators are applied for the constructor.
  4. Class Decorators are applied for the class.

JavaScript ES6 & Typescript

Class Decorator

// declaration
function sealed(constructor: Function) {
  Object.seal(constructor);
  Object.seal(constructor.prototype);
}

// evaluation
@sealed
class BugReport {
  type = "report";
  title: string;
 
  constructor(t: string) {
    this.title = t;
  }
}

JavaScript ES6 & Typescript

Method Decorator

// declaration
function enumerable(value: boolean) {
  return function (target: any, propertyKey: string, descriptor: PropertyDescriptor) {
    descriptor.enumerable = value;
  };
}

// evaluation
class Greeter {
  greeting: string;
  constructor(message: string) {
    this.greeting = message;
  }
 
  @enumerable(false)
  greet() {
    return "Hello, " + this.greeting;
  }
}

JavaScript ES6 & Typescript

Accessor Decorator

// declaration
function configurable(value: boolean) {
  return function (target: any, propertyKey: string, descriptor: PropertyDescriptor) {
    descriptor.configurable = value;
  };
}

// evaluation
class Point {
  private _x: number;
  private _y: number;
  constructor(x: number, y: number) {
    this._x = x;
    this._y = y;
  }
 
  @configurable(false)
  get x() {
    return this._x;
  }
 
  @configurable(false)
  get y() {
    return this._y;
  }
}

JavaScript ES6 & Typescript

Property Decorator

// declaration
import "reflect-metadata";

const formatMetadataKey = Symbol("format");

function format(formatString: string) {
  return Reflect.metadata(formatMetadataKey, formatString);
}

// evaluation
class Greeter {
  @format("Hello, %s")
  greeting: string;
  constructor(message: string) {
    this.greeting = message;
  }
  greet() {
    let formatString = getFormat(this, "greeting");
    return formatString.replace("%s", this.greeting);
  }
}

JavaScript ES6 & Typescript

Parameter Decorator

// declaration
import "reflect-metadata";

const requiredMetadataKey = Symbol("required");
 
function required(target: Object, propertyKey: string | symbol, parameterIndex: number) {
  let existingRequiredParameters: number[] = Reflect.getOwnMetadata(requiredMetadataKey, target, propertyKey) || [];
  existingRequiredParameters.push(parameterIndex);
  Reflect.defineMetadata( requiredMetadataKey, existingRequiredParameters, target, propertyKey);
}

// evaluation
class BugReport {
  type = "report";
  title: string;
 
  constructor(t: string) {
    this.title = t;
  }
 
  print(@required verbose: boolean) {
    if (verbose) {
      return `type: ${this.type}\ntitle: ${this.title}`;
    } else {
     return this.title; 
    }
  }
}

Type definition files

JavaScript ES6 & Typescript

JavaScript ES6 & Typescript

A declaration file provides a way to declare the existence of some types or values without actually providing implementations for those values.

JavaScript ES6 & Typescript

.ts files are implementation files that contain types and executable code. These are the files that produce .js outputs, and are where you’d normally write your code.

.d.ts files are declaration files that contain only type information. These files don’t produce .js outputs; they are only used for typechecking.

TypeScript has two main kinds of files.

JavaScript ES6 & Typescript

Built-in type definitions

TypeScript includes for most of the common JavaScript constructs like string, object etc.

target

Version of JavaScript to load definitions for like es6 or es2015.

lib

A more fine grained settings to target specific functionalities of JavaScript like ES2015.Proxy

JavaScript ES6 & Typescript

External definitions

For non-standard APIs there are multiple ways of working with dts files.

@types

These are types for libraries that most of them make available and can be installed via npm

Own

We can also declare our own modules to either or just declare a module name to silence warnings.

Q&A

JavaScript ES6 & Typescript

Thank you!

JavaScript ES6 & Typescript

By Alex Albu

JavaScript ES6 & Typescript

  • 424