Modern JavaScript
& Typescript
slides: bit.ly/3sMgSS3
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
Andrei Antal
@andrei_antal
- frontend engineer, since i can remember
- currently doing consulting and training @JSLeague
- web & JS technologies enthusiast
- UX and accessibility passionate
- perpetual learner
Frontend Developer, Training Manager @ JsLeague
organizer for ngBucharest
@ngBucharest
groups/angularjs.bucharest
YOU?
- brief intro
- experience with course topics
- expectations from the course
Overview
Modern JavaScript & Typescript
Modern JavaScript & Typescript
Agenda for the course
Day 1: Modern JS and TypeSctipt
Day 2: Intro to Angular - part1
Components & Modules
Day 3: Intro to Angular - part2
Services, Observables & RxJS, HTTP
Day 4: Intro to Angular - part3
Routing, Forms
Agenda for today
// Modern ES
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
Modern JavaScript & Typescript
Agenda for today
// Typescript
Overview
Type System
Classes
Interfaces
Union / Intersection types
Enums
Generics
Decorators
Modern JavaScript & Typescript
What is JavaScript?
Modern JavaScript & Typescript
JavaScript (JS) is a high-level, lightweight, interpreted (or just-in-time compiled) multi-paradigm (supporting event-driven, functional, and imperative styles) programming language with dynamic typing, prototype-based object-orientation, and first-class functions.
1. In HTML, within a <script> tag
2. Linking to an external file
<script>
// JS Code
</script>
<script src="path/to/my/script.js"></script>
// script.js
// JS CODE
Modern JavaScript & Typescript
Including JavaScript in your web page
-
Handling browsers that don’t support JavaScript
<noscript>
Your browser does not support JavaScript so page
functionality will be significantly reduced.
</noscript>
-
Controlling loading style of script files:
- async - script is executed asynchronously while the page continues the parsing.
- defer - script is executed after the page has finished parsing.
Modern JavaScript & Typescript
Modern JavaScript & Typescript
-
1995
- May - Mocha invented by Brendan Eich at Netscape (in 10 days!)
- September - renamed to LiveScript
- December - renamed to JavaScript (because JAVA...)
- 1996 - ECMA takes JavaScript to standardization
- 1997 - ECMA-262 (ECMAScript)
- 1998 - ECMAScript 2
- 1999 - ECMAScript 3
- 2005 - Mozilla and Macromedia began to work on ECMAScript 4 - never released => ECMAScript 3.1
- 2009 - ES5 emerges from previous discussions
-
2015 - ES6 standard
- Version names based on year of release (ES2015, ES2016...)
EcmaScript Vesion | Year |
---|---|
ES6 | 2015 |
ES7 | 2016 |
ES8 | 2017 |
ES9 | 2018 |
ES10 | 2019 |
ES11 | 2020 |
ES12 | 2021 |
ES.NEXT | -Infinity |
Modern JavaScript & Typescript
How can I use it?
Modern JavaScript & Typescript
Modern JavaScript & Typescript
Cross-browser solution:
Babel
Modern JavaScript & Typescript
Modern JavaScript
code: bit.ly/3r9pQIo
Block scope variables
Modern JavaScript & Typescript
function getColor(condition) {
if (condition) {
var value = "blue";
// other code
return value;
} else {
return null;
}
}
ES5 Variable Hoisting
Modern JavaScript & Typescript
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
code: bit.ly/45CLPXg
Typescript Overview
JavaScript ES6 & Typescript
JavaScript ES6 & Typescript
JavaScript ES6 & Typescript
JavaScript ES6 & Typescript
JavaScript ES6 & Typescript
JavaScript ES6 & Typescript
- Open Source and Open Development
- Closely track ECMAScript standard
- Innovate in the type system
- Best-of-breed tooling
- Continually lower barrier to entry
- Great community support
JavaScript ES6 & Typescript
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
// initializing
tsc --init
// 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
Arrays vs Tuples
const person = [36, "Andrei"]
const [age, name] = person
const person: [number, string] = [36, "Andrei"]
const [age, name] = person
const person = [36, "Andrei"] as const
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 });
JavaScript ES6 & Typescript
unknown type
let value: unknown;
value = true; // OK
value = 42; // OK
value = "Hello World"; // OK
value = []; // OK
value = {}; // OK
value = Math.random; // OK
value = null; // OK
value = undefined; // OK
value = new TypeError(); // OK
value = Symbol("type"); // OK
let value: unknown;
let value1: unknown = value; // OK
let value2: any = value; // OK
let value3: boolean = value; // Error
let value4: number = value; // Error
let value5: string = value; // Error
let value6: object = value; // Error
let value7: any[] = value; // Error
let value8: Function = value; // Error
JavaScript ES6 & Typescript
unknown type
let value: any;
value.foo.bar; // OK
value.trim(); // OK
value(); // OK
new value(); // OK
value[0][1]; // OK
let value: unknown;
value.foo.bar; // Error
value.trim(); // Error
value(); // Error
new value(); // Error
value[0][1]; // Error
JavaScript ES6 & Typescript
unknown type
function stringifyForLogging(value: unknown): string {
if (typeof value === "function") {
// Within this branch, `value` has type `Function`,
// so we can access the function's `name` property
const functionName = value.name || "(anonymous)";
return `[function ${functionName}]`;
}
if (value instanceof Date) {
// Within this branch, `value` has type `Date`,
// so we can call the `toISOString` method
return value.toISOString();
}
return String(value);
}
JavaScript ES6 & Typescript
type guards
function isNumberArray(value: unknown): value is number[] {
return (
Array.isArray(value) && value.every(element => typeof element === "number")
);
}
const unknownValue: unknown = [15, 23, 8, 4, 42, 16];
if (isNumberArray(unknownValue)) {
// Within this branch, `unknownValue` has type `number[]`,
// so we can spread the numbers as arguments to `Math.max`
const max = Math.max(...unknownValue);
console.log(max);
}
JavaScript ES6 & Typescript
type assertions
const value: unknown = "Hello World";
const someString: string = value as string;
const otherString = someString.toUpperCase(); // "HELLO WORLD"
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
- Parameter Decorators, followed by Method, Accessor, or Property Decorators are applied for each instance member.
- Parameter Decorators, followed by Method, Accessor, or Property Decorators are applied for each static member.
- Parameter Decorators are applied for the constructor.
- 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!
Modern JavaScript & Typescript
By Andrei Antal
Modern JavaScript & Typescript
- 318