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 

Block scope variables

Template strings

Enhanced Objects

Arrow functions

Classes

Destructuring

Spread and rest

Modules

Iterators

Async / Await

// Typescript

Overview

Type System

Classes

Interfaces

Union / Intersection types

Enums

Generics

Decorators

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

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;
}

PersonType.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

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 = {c: 'd', ...obj1};

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

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'));

JavaScript ES6 & Typescript

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

Typescript Overview

JavaScript ES6 & Typescript

JavaScript ES6 & Typescript

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

JavaScript ES6 & Typescript

How does it work?

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

interface Person {
  age: number;
}

class John implements Person {
  constructor(public age: number) {}
}

let pers1: Person = new John(32);
let pers2: John = {age: 32};

Structural typing

Classes

JavaScript ES6 & Typescript

JavaScript ES6 & Typescript

We can use types in classes

Same as ES6 classes

Interfaces

JavaScript ES6 & Typescript

JavaScript ES6 & Typescript

interface Node {
  (x: string): string;       // callable

  new (label: string): Node; // constructor

  children: Node[];          // property

  numChildren(): number;     // method
}

All types are expressible as interfaces

JavaScript ES6 & Typescript

interface Data {
  [key: string]: boolean;
}

const d: Data = {};
d['key1'] = true;
console.log(d['key1']); // true

Interfaces allow for typing object index signature

Union / Intersection types

JavaScript ES6 & Typescript

JavaScript ES6 & Typescript

type primitive = string | number | boolean;

type object = String | Number | Boolean;

type dataType = primitive | object;

Union

JavaScript ES6 & Typescript

type MyDiv = HTMLDivElement & {customDivAttr: string};

Intersection

JavaScript ES6 & Typescript

type el = HTMLAnchorElement | HTMLImageElement;

const location = (d: el) => {
  if(d instanceof HTMLAnchorElement) {
    return d.href;
  }

  return d.src;
}

Usage: narrows casting

JavaScript ES6 & Typescript

function one(x: string | undefined) { ... }
one();
// Error: requires one argument

function two(x?: string) { ... }
two();
// It's ok

Optional arguments

Enums

JavaScript ES6 & Typescript

JavaScript ES6 & Typescript

enum Card {
  CLUBS = 1, // Subsequent values are incrm. from 1
  DIAMONDS,
  HEARTS,
  SPADES
}

enum Colors {
  RED = '#FF0000',
  GREEN = '#00FF00',
  BLUE = '#0000FF'
}

let myCard = Card.HEARTS; // 3
let red = Colors.RED; // #FF0000

Enums assign a numbers/strings to each symbol

Generics

JavaScript ES6 & Typescript

JavaScript ES6 & Typescript

class GenericQueue<T> {
    enqueue(value: T) : void {/*enqueue value */}
    dequeue() : T {/*dequeue value*/}
}

let q2 = new GenericQueue<string>();
q2.enqueue("Hello");
q2.enqueue(17); // compilation error

let q3 = new GenericQueue<Animal>();
q3.enqueue(new Animal("Max"));
q3.enqueue("Hello"); // compilation error

Decorators

JavaScript ES6 & Typescript

JavaScript ES6 & Typescript

function ClassDecoratorParams(param1: number, param2: string) {
    return function(
        target: Function // The class the decorator is declared on
        ) {
        console.log("ClassDecoratorParams(" + param1 + ", '" + 
            param2 + "') called on: ", target);
    }
}

@ClassDecoratorParams(1, "a")
@ClassDecoratorParams(2, "b")
class ClassDecoratorParamsExample {
}

// ClassDecoratorParams(2, 'b') called on:  function ClassDecoratorParamsExample() {}
// ClassDecoratorParams(1, 'a') called on:  function ClassDecoratorParamsExample() {}

Functions that are invoked with a prefixed @ symbol, and immediately followed by a class, parameter, method or property.

Q&A

JavaScript ES6 & Typescript

Thank you!