TypeScript
JavaScript that scales
Ali Arafati
@CitizenOfTheWeb ali.arafati@axis.com

[][(![]+[])[+[]]+([![]]+[][[]])[+!+[]+[+[]]]+(![]+[])[!+[]+!+[]]+(!![]+[])[+[]]+(!![]+[])[!+[]+!+[]+!+[]]+(!![]+[])[+!+[]]][([][(![]+[])[+[]]+([![]]+[][[]])[+!+[]+[+[]]]+(![]+[])[!+[]+!+[]]+(!![]+[])[+[]]+(!![]+[])[!+[]+!+[]+!+[]]+(!![]+[])[+!+[]]]+[])[!+[]+!+[]+!+[]]+(!![]+[][(![]+[])[+[]]+([![]]+[][[]])[+!+[]+[+[]]]+(![]+[])[!+[]+!+[]]+(!![]+[])[+[]]+(!![]+[])[!+[]+!+[]+!+[]]+(!![]+[])[+!+[]]])[+!+[]+[+[]]]+([][[]]+[])[+!+[]]+(![]+[])[!+[]+!+[]+!+[]]+(!![]+[])[+[]]+(!![]+[])[+!+[]]+([][[]]+[])[+[]]+([][(![]+[])[+[]]+([![]]+[][[]])[+!+[]+[+[]]]+(![]+[])[!+[]+!+[]]+(!![]+[])[+[]]+(!![]+[])[!+[]+!+[]+!+[]]+(!![]+[])[+!+[]]]+[])[!+[]+!+[]+!+[]]+(!![]+[])[+[]]+(!![]+[][(![]+[])[+[]]+([![]]+[][[]])[+!+[]+[+[]]]+(![]+[])[!+[]+!+[]]+(!![]+[])[+[]]+(!![]+[])[!+[]+!+[]+!+[]]+(!![]+[])[+!+[]]])[+!+[]+[+[]]]+(!![]+[])[+!+[]]]((![]+[])[+!+[]]+(![]+[])[!+[]+!+[]]+(!![]+[])[!+[]+!+[]+!+[]]+(!![]+[])[+!+[]]+(!![]+[])[+[]]+(![]+[][(![]+[])[+[]]+([![]]+[][[]])[+!+[]+[+[]]]+(![]+[])[!+[]+!+[]]+(!![]+[])[+[]]+(!![]+[])[!+[]+!+[]+!+[]]+(!![]+[])[+!+[]]])[!+[]+!+[]+[+[]]]+[+!+[]]+(!![]+[][(![]+[])[+[]]+([![]]+[][[]])[+!+[]+[+[]]]+(![]+[])[!+[]+!+[]]+(!![]+[])[+[]]+(!![]+[])[!+[]+!+[]+!+[]]+(!![]+[])[+!+[]]])[!+[]+!+[]+[+[]]])()
JavaScript
We Bare Bears
JavaScript Evolution;
function getTallPeopleCount(people) {
var count;
for (var i = 0; i < people.length; i++) {
if (people.height >= 180) {
count++;
}
}
return count;
}
function getTallPeopleCount(people) {
return people.filter(function (person) {
return person.height >= 180;
}).length;
}
function getTallPeopleCount(people) {
return people.filter(person => person.height >= 180).length;
}
ECMAScript 3 - 1999
ECMAScript 5 - 2009
ECMAScript 6 - 2015
JavaScript Evolution: backward compatible
if (!Array.prototype.filter) {
Array.prototype.filter = function(fun/*, thisArg*/) {
'use strict';
if (this === void 0 || this === null) {
throw new TypeError();
}
var t = Object(this);
var len = t.length >>> 0;
if (typeof fun !== 'function') {
throw new TypeError();
}
var res = [];
var thisArg = arguments.length >= 2 ? arguments[1] : void 0;
for (var i = 0; i < len; i++) {
if (i in t) {
var val = t[i];
if (fun.call(thisArg, val, i, t)) {
res.push(val);
}
}
}
return res;
};
}
function getTallPeopleCount(people) {
return people.filter(function(person) {
return person.height >= 180;
}).length;
}
Polyfill (or shim)
ES5 Shim: https://github.com/es-shims/es5-shim
Compatibility table for ES5: http://kangax.github.io/compat-table/es5/

ECMAScript 6:
- Promise
- Map & Set
- Symbols (~)
- Prototypes
- Number
- String
- Array
ES6 Shim: https://github.com/es-shims/es6-shim
1
JavaScript Evolution: backward compatible
Syntatic features
function getTallPeopleCount(people) {
return people.filter(person => person.height >= 180).length;
}
function getTallPeopleCount(people) {
return people.filter(function (person) {
return person.height >= 180;
}).length;
}


Transpilers (i.e. compilers)
2
JavaScript Evolution: backward compatible
ECMAScript 5 features (getter/setter)
var object = {
get a() { return object._a; }
set a(value) { object._a = value; }
};ECMAScript 6 features (proxies)
var handler = {
get: function(target, name) {
return name in target ?
target[name] :
37;
}
};
var p = new Proxy({}, handler);
p.a = 1;
p.b = undefined;
console.log(p.a, p.b); // 1, undefined
console.log('c' in p, p.c); // false, 37IE8 and lower 😞

3
no ES5 support 😞
no ES3 support


JavaScript - State of the art
- ECMAScript features
- Check compatibility
- things you can use out of the box
- things you need shim/polyfills for
- things you can transpile
- things you cannot use in the target platform
- Use a transpiler
- Use shim/polyfills
JavaScript Feature Gap
ES3
ES5
ES 2015 - ES6
ES 2016
ES 2017
ES 2018
ES 2019
1999
2009
state of the art JavaScript
NodeJS
browsers
legacy
Smart phones
Node 6.5

Tooling is important
Mistakes;


image source: http://www.lexolutionit.com/article_details.php?id=337
- Cosmetic
- Typos
- Deprecate API calls
- Dangerous API calls
- Bad practices
const bad = new String('WAT?');
const areWeGood = !!condition;
const howFar = ~bad.indexOf('?');
const cats = localStorage.getItem(STORAGE_KEY);
const result = eval('2 + 12');
setTimeout('function() { console.log(1) }');
setInterval(() => { checkTheWeather(); }, 1000);// invalid
$rootScope.$on('destroy', function () {
// ...
}); // error: You probably misspelled $on("$destroy").// valid
angular.module('myModule').controller('MyController', function () {
// ... MyCtrl or MyControler or Mycontroller is not valid
});
// invalid
$rootScope.$on('event', function () {
// ...
}); // error: The "$on" call should be assigned to a variable,
// in order to be destroyed during the $destroy eventESLint
function getTallPeopleCount(people) {
return people.filter(person => persom.height >= 180).length;
}
function getTallPeopleCount(people) {
return people.filter(person => person.hieght >= 180).lenght;
}
function sortPeopleByName(people) {
return people.sort(function(p1, p2) {
p1.name.localCompare(p2.name);
});
}
1
2
Mistakes;
function getGamePlayersAsync() {
return Game.waitForConnection(connection => {
return connection.players;
});
}
3
function getGamePlayersAsync() {
return Game.waitForConnection(connection => {
console.log(connection);
return connection.players;
});
}
function getTallPeopleCount(people) {
return people.filter(person => persom.height >= 180).length;
}
1
DEMO;
TypeScript:
JavaScript that scales
A statically typed superset of JavaScript that compiles to plain JavaScript.
If it looks like a duck and quacks like a duck, it's a duck
// person.ts
interface Person {
name: string;
}
function getName(person: Person) {
return person.name;
}// index.ts
let employee = {
name: 'Ali',
company: 'Axis'
};
getName(employee); // OK
getName({ name: 'Donald Duck' }); // OK
getName({ name: 13 }); // ERROR
getName({ blah: 'yada' }); // ERROR
let movie = getMovieByName('Fantastic Beasts and Where to Find Them');
getName(movie); // OK
Demo;
Control Flow Base Type Analysis
With TypeScript 2.0, the type checker analyses all possible flows of control in statements and expressions to produce the most specific type possible (the narrowed type) at any given location for a local variable or parameter that is declared to have a union type.
function normalize(id: string | number) {
if (typeof id === 'number') {
return id;
}
return +id.split(':')[0];
}
normalize(1234);
normalize('1234:xp:12')Type: Any
let k;
k = '';
k.substr(1);
k = 12.34;
k.toFixed();function f1() {
let x = [];
x.push(5);
if (cond()) {
x[1] = "hello";
}
x.unshift(true);
return x;
}StrictNullCheck
string
"foo"
""
undefined
null
number
42
-3.1415
undefined
null
NaN
boolean
true
false
undefined
null
StrictNullCheck (demo)
string
"foo"
""
undefined
null
number
42
-3.1415
NaN
boolean
true
false
Literal Types
var r = validateSomething();
if (r.success === true) {
r.data;
} else {
r.errorNo;
r.message;
}type Result<T> =
{ success: true; data: T } |
{ success: false; errorNo: number; message: string; };
function validateSomething(): Result<number>;const KEY = '[SECURE KEY]';
const PI = 3.14;
const flag = true;
const x = cond() ? PI : '3.1415';
let bit: 1 | 0;
let env: 'production' | 'development';
Tagged union types
interface UserRoot {
type: 'UNIL' | 'LDAP';
}
interface LDAP extends UserRoot {
type: 'LDAP';
username: string;
name: string;
groups: string[];
roles: string;
}
interface UNIL extends UserRoot {
type: 'UNIL';
username: string | number;
hid: string;
}
type User = LDAP | UNIL;interface User {
type: 'UNIL' | 'LDAP'; // or string
username: string | number;
hid?: string; // note the tentetive type
name?: string;
groups?: string[];
roles?: string;
}
Tagged union types
document.querySelector('script').src; // OK
document.querySelector('a').href; // OKinterface NodeSelector {
querySelector(selectors: 'a'): ElementAnchorElement | null;
querySelector(selectors: 'script'): ElementScriptElement | null;
querySelector(selectors: string): Element | null;
// ...
}Tagged union types
interface AddToCartAction {
type: 'ADD_TO_CART';
product: {
id: number;
name: string;
};
}
interface RemoveFromCartAction {
type: 'REMOVE_FROM_CART';
productId: number;
}
type ShoppingCartActions = AddToCartAction | RemoveFromCartAction;export default (state = initialState, action: ShoppingCartActions) => {
switch (action.type) {
case ADD_TO_CART:
// action is AddToCartAction
break;
case REMOVE_FROM_CART:
// action is RemoveFromCartAction
break;
default: return state;
}
}
Write less; do more;
-
async/await
-
decorators
async/await
function getUser() {
return request('user').then(user => {
if (user.hasProjects) {
return request('projects').then(projects => {
return {
...user,
projects
};
});
}
return user;
});
}getUser().then(user => {
if (user.hasProjects) {
user.projects; // OK
} else {
user.projects; // Empty, or maybe undefined
}
});async/await
function getUser() {
return request('user').then(user => {
if (user.hasProjects) {
return request('projects').then(projects => {
return {
...user,
projects
};
});
}
return user;
});
}async function getUser() {
let user = await request('user');
if (user.hasProjects) {
user.projects = await request('projects');
}
return user;
}
Decorators
import { deprecate } from 'core-decorators';
class Person {
@deprecate
facepalm() {}
@deprecate('We stopped facepalming')
facepalmHard() {}
@deprecate('We stopped facepalming', { url: 'http://knowyourmeme.com/memes/facepalm' })
facepalmHarder() {}
}
import { throttle } from 'core-decorators';
class Editor {
content = '';
@throttle(500, { leading: false })
updateContent(content) {
this.content = content;
}
}function throttle(timeout, config) {
return function(target, methodName) {
const originalMethod = target[methodName];
let h;
target.defineProperty(target, methodName, {
value: function(...args) {
if (h) { clearTimeout(h); }
h = setTimeout(() => {
originalMethod.apply(this, args);
}, timeout);
}
});
}
}class Post {
public id: number;
public title: string;
public content: string;
@JSON('writters.author')
public author: string;
@JSON('writters.editor')
public editor: string;
}// Post json structure
{
id: 1,
title: 'foo',
content: 'bar',
writters: {
author: 'buzz',
editor: 'bizz'
}
}var Post = (function () {
function Post() {
}
return Post;
}());
__decorate([
JSON('writters.author'),
__metadata("design:type", String)
], Post.prototype, "author");
__decorate([
JSON('writters.editor'),
__metadata("design:type", String)
], Post.prototype, "editor");emitDecoratorMetadata
@API({
endpoint: '/posts/:id',
canUpdate: (user, post) => user.canPost(post),
canDelete: (user, post) => user.canDelete(post)
})
class Post {
public id: number;
public title: string;
public content: string;
@JSON('writters.author')
public author: string;
@JSON('writters.editor')
public editor: string;
@API({
endpoint: (post) => `/posts/${post.id}/comments`
})
@JSON('user_comments')
public comments: Comment[];
}Demo
Angular 1.x
NodeJS
React
When/Why TypeScript
- Large JavaScript codebase
- Large Teams
- Fully compatible/adaptable with EcmaScript Next
- Types are optional - gradual move - partial
- Easy refactoring
- Debug natively - Sourcemap
- Open Source - Active community
- Toolings (IDE - LINTER - Language Service)
- Already available typings for third party libs and frameworks
- Typings can give you great insights on the code
- Typings can provide great documentation for the code
- Share typings in universal applications
Who is using TypeScript
- Google
- Angular 2 (Angular 4, ...)
- RxJS, Ionic, Cycle.js, Blueprint, Dojo, NativeScript, Plottable
- Microsoft
- Visual Studio Code

Questions?
Ali Arafati
@CitizenOfTheWeb https://slides.com/aliai/typescript
TypeScript: JavaScript that scales
By Ali Arafati
TypeScript: JavaScript that scales
There are always certain challenges when it comes to staying productive in a large JavaScript codebase. It becomes more difficult to see how things are connected, while introducing changes may bring about more bugs and pain than it actually contributes to the system. Giant leading companies have tried to address this fact by effortlessly working on various toolings: Google with Closure Compiler, Facebook with Flow and Microsoft with TypeScript. While all these tools aim at a similar premise, in my opinion, TypeScript is ahead in this specific game by far. TypeScript is about making JavaScript scale. It doesn't aim at replacing JavaScript and certainly not changing the way we love and write in the language. TypeScript is where you can optionally define static types while staying in the current state of the art JavaScript, and adhere to them as your principles. It's an open-source project with an active community, and it also offers great toolings cross-platform.
- 1,067