Static e runtime checking in JavaScript

TypeScript, Flow, AtScript

## Giulio Canti ### web developer @ madai ltd ### Matematico
## Sommario 1. ### Teoria (degli insiemi) - ### Pratica (TypeScript, Flow, AtScript) - ### Terzo tempo
## Introduzione 1. ### cosa sono i tipi? - ### a cosa servono? - ### **static** type checking? - ### **runtime** type checking?

Linguaggio dinamico


function Person(name, surname, age) {
	this.name = name;
	this.surname = surname;
	this.age = age;
}
					

var person = new Person(true, true, true); // WTF!
					

Static type checking


function Person(name: string, surname: string, age: number) {
	this.name = name;
	this.surname = surname;
	this.age = age;
}
					

var person = new Person(true, true, true); // TypeScript complains
					

var person = new Person("", null, -1); // WTF!
					

Runtime type checking


function Person(name, surname, age) {

	assert(typeof name === 'string' && name);
	assert(typeof surname === 'string' && surname);
	assert(typeof age === 'number' && age >= 0);

	this.name = name;
	this.surname = surname;
	this.age = age;
}
					

var person = new Person("", null, -1); // throws
					
## Predicato Una funzione con arità 1 e che restituisce un booleano ``` // (x: any) => boolean function isString(x) { return typeof x === 'string'; } ``` Alcune API che utilizzano i predicati... ``` Array.prototype.filter Array.prototype.some Array.prototype.every [1, 'a', 2].filter(isString); // => ['a'] ```
## `assert()` ``` function assert(guard, message) { if (guard !== true) { debugger; // <= potete ispezionare lo stack throw new TypeError(message || 'assert failed'); } } ```
## Un `TypeError` è per definizione un baco

asserts !== validations

# Teoria (e runtime checking)

Per Martin-Löf

Logician, philosopher, and mathematical statistician (1942)

# Insiemi "Un insieme è una collezione di oggetti" Il concetto di insieme è considerato primitivo ed intuitivo
## Tipi ⇄ Insiemi Esempio: il tipo `Boolean` è l'insieme che contiene i due elementi `true` e `false`
## Come rappresentare un insieme in JavaScript?
## Rappresentazione estensiva ``` // ok per insiemi finiti e..."piccoli"! var Boolean = [true, false]; ```
## Funzione caratteristica Ad un insieme `A` posso associare il predicato `isA` `isA(x) = true`, se `x ∈ A` `isA(x) = false`, se `x ∉ A`
Ad ogni predicato `isA` posso associare un insieme `A = { x ∈ Any | isA(x) = true }` ove `Any` è l'insieme universo
## Insiemi ⇄ Predicati
## Rappresentazione intensiva ``` function isNumber(x) { return typeof x === 'number'; } ```

Insieme universo: Any


function isAny(x) { return true; }
					

Insieme vuoto


function isEmptySet(x) { return false; }
					

Insieme di tutti gli array


Array.isArray
					
## Sottoinsieme `A ⊆ B` se per ogni `a ∈ A` vale `a ∈ B` ``` function isAge(x) { return isNumber(x) && x >= 0; } ``` ( Subtypes )
## Intersezione `A ∩ B = { x ∈ Any | x ∈ A ∧ x ∈ B }` ``` function isAiB(x) { return isA(x) && isB(x); } ``` ( Intersection types )
## Unione `A ∪ B = { x ∈ Any | x ∈ A ∨ x ∈ B }` ``` function isAuB(x) { return isA(x) || isB(x); } ``` ( Sum types )
## Prodotto cartesiano `A ⨯ B = { (a, b) | a ∈ A, b ∈ B }` ``` // tuple function isAxB(xs) { return Array.isArray(xs) && // è un array xs.length === 2 && // è una generica tuple2 isA(xs[0]) && // controllo A isB(xs[1]); // controllo B } ``` ( Product types )
### **sum** types + **product** types = ADT (algebraic data types)
altri prodotti cartesiani notevoli: ### Array `A1 ⨯ A2 ⨯ ... = { (a1, a2, ... ) | ai ∈ Ai}` ``` Array.isArray ``` ### Liste `A* = { (a_1, a_2, ... ) | a_i ∈ A}` ``` function isListOfNumbers(x) { return Array.isArray(x) && x.every(isNumber); } ```
### Type combinators ``` function list(isA) { var p = function (x) { return Array.isArray(x) && x.every(isA); }; p.kind = 'list'; // <- allows "runtime type introspection" p.type = isA; return p; } var isListOfNumbers = list(isNumber); var isMatrixOfNumbers = list(list(isNumber)); ... ``` [https://github.com/gcanti/tcomb](https://github.com/gcanti/tcomb) [https://github.com/gcanti/flowcheck](https://github.com/gcanti/flowcheck)
### Le funzioni sono insiemi `C = { f: A -> B } ⊆ A ⨯ B` se `(a, b1)` e `(a, b2) ∈ C` allora `b1 = b2` ``` // (a: int) => int function f(a) { return 2 * a; } "syntactic sugar" per: (1, 2) (2, 4) (3, 6) ... ```

Oggetti?

Sono prodotti cartesiani mimetizzati tra funzioni e tuple...

### Oggetti come funzioni (`Map`) ``` JavaScript Math ---------------------------------------- var f = { f: string -> number "a": 1, "a" -> 1 "b": 2, "b" -> 2 "c": 3 "c" -> 3 }; f["b"] -> 2 f("b") -> 2 ```
### Oggetti come tuple ``` object tuple ---------------------------------------- { [ "name": "Giulio", "Giulio", "surname": "Canti", "Canti", "age": 41 41 } ] function Person(name, surname, age /* <- tuple */) { this.name = name; this.surname = surname; this.age = age; /* ^-- object */ } ```

Fine primo tempo

Insiemi Tipi
universo Any
sottoinsiemi number, string, ...
subtypes
intersezioni intersection types
unioni (disgiunte) sum types
prodotti cartesiani product types
funzioni
f: A -> B
function types
(a: A) => B
# TypeScript
## Che cos'è? - ### Superset di JavaScript - ### Static type checker - ### Compiler (ES6)

"Optional typing"

## Stats - Microsoft - parte nel 2012 - v1.4 - Anders Hejlsberg (C#, Delphi, Turbo Pascal) - GitHub issues: 530 open, 953 closed
## Setup `npm install typescript` ## Usage `tsc input.ts --out output.js`
## Type inference ``` var i = 0; // i è di tipo `number` function f() { return "hello"; // f restituisce un tipo `string` } var x: number = f(); // error TS2322: Type string is not assignable to type number ```
## Primitive ``` // var name: type [= value]; var all: any; var num: number; var str: string; var bool: boolean; ```
## Attenzione! ``` var a: number = null; // ok per TypeScript var b: number = undefined; // ok per TypeScript ```
## Intersection types ``` // User-defined data type (1/2) interface Point { x: number; y: number; } var p: Point: {x: 1, y: 2}; ``` ``` interface A {} interface B {} interface C extends A, B {} // <- intersection type ```
## Sum types ``` // dalla v1.4 var x: number | string = 1; ```
## Product types
### Tuples ``` var tuple: [string, number] = ['a', 1]; ``` ### Lists ``` var list: Array<number> = [1, 2, 3]; var list: number[] = [1, 2, 3]; ``` ### Arrays ``` var arr: any[] = ['a', 2, 3]; ```
### Objects ``` var point: {x: number; y: number;} = {x: 1, y: 2}; ``` ### Mappe ``` var mymap: {[key: string]: number;} = { a: 1, b: 2, c: 3 }; ```
## Classi (1/2) ``` class Person { name: string; surname: string; age: number; constructor(name: string, surname: string, age: number) { this.name = name; this.surname = surname; this.age = age; } } var p = new Person(true, true, true); // error TS2345: Argument of type 'boolean' is not assignable // to parameter of type 'string' ```
## Classi (2/2) ``` // modifiers: public, private, protected class Person { constructor( public name: string, public surname: string, public age: number) { } } ```
## Funzioni ``` function len(s: string): number { return s.length; } ``` ES6 fat arrow ``` var len = (s: string): number => s.length; ``` Function types ``` function asyncOp(callback: (result: string) => any): void { ... } ```
### Type alias ``` // User-defined data type (2/2) type StringOrNumber = string | number; type Text = string | { text: string; }; type Coordinates = [number, number]; ```
## Moduli
### Internal modules ``` module A { module B { export var value = 1; } } console.log(A.B.value); // => 1 ```
### External modules ``` // file mylib.ts var b = 1; export = b; // same as module.exports = b; ``` ``` // file myapp.ts import b = require('./mylib'); ```
## Definition files ``` /// <reference path="path/to/jquery.d.ts" /> ``` https://github.com/borisyankov/DefinitelyTyped
### Polimorfismo (AKA Generics) ``` function map<A>(xs: A[], f: (x: A) => A): A[] { ... } map([1, 2, 3], (x) => 'a'); // error! ``` - funzioni - classi - interfaces
## Compiler flags - `--watch` - `--sourceMap` - `--noImplicitAny` - `--target ES3-5-6` - `--declaration`
## Pros - **Language Specification** (161 pagine) - `--noImplicitAny` - DefinitelyTyped - IDE support (VS, WebStorm, Sublime)
## Cons - gestione manuale dei definition files - nullable types - no JSX support - type erasure (!?)
## Type erasure > ...e tutti quei momenti andranno perduti nel tempo come lacrime nella pioggia... — Blade Runner
# Flow
## Che cos'è? - ### Superset di JavaScript - ### Static type checker
## Stats - Facebook - novembre 2014 - v0.5 - GitHub issues: 153 open, 86 closed
### Compatibile con TypeScript? # No :-(
Flow TypeScript
Object Object - (primitives | Function)
T T - (null | void)
?T T
A & B interfaces+ function overloading
type alias (generics) interfaces + type alias
## Setup Download Flow... ## Usage ``` flow check # oppure... flow start flow check ```
### .flowconfig ``` [ignore] [include] ./node_modules [libs] ...definition files here [options] ```
### Type annotations nei commenti ``` /* @flow */ function sum(a/*: number*/, b/*: number*/) { return a + b; } sum('a', 2); // error: string This type is incompatible with number ```
### Classes ``` class Person { name: string; surname: string; age: number; constructor(name, surname, age) { this.name = name; this.surname = surname; this.age = age; } } ```
# React
### JSX ``` var HelloMessage = React.createClass({ render() { return <div>hello</div>; } }); React.render(<Hello />, document.body); // error: React element: Hello Unknown global name ```
### PropTypes (1/2) ``` var Foo = React.createClass({ propTypes: { foo: React.PropTypes.number.isRequired } }); React.createElement(Foo, { foo: "not a number" }); // string This type is incompatible with number ```
### PropTypes (2/2) ``` // v0.13 class HelloMessage extends React.Component { constructor(props: { name: string; }) { super(props); } render() { return <div>Hello {this.props.name}</div>; } } React.render(new HelloMessage({name: 1}), document.body); // error: number This type is incompatible with string ```
## Pros - aggressive type inference - non nullable types - enums come stringhe - Type annotations nei commenti - better commonjs support - JSX support
## Cons - cryptic error messages - no Windows - no `--noImplicitAny` - no `import` o `export` - type erasure
# AtScript
## Che cos'è? - ### Superset di JavaScript - ### compiled by Traceur
## Risorse - AtScript Primer - https://github.com/angular/atscript-playground - https://github.com/angular/angular (v2.0)
### Runtime Type Assertions ```js // AtScript... class MyClass { methodA(name: string): int { var length: int = name.length; return length; } } ``` ```js // ... compiles to ES6 import * as rtts from 'rtts'; // assert library (customizable) class MyClass { methodA(name) { rtts.type(name, rtts.string); var length = rtts.type(name.length, rtts.int); return rtts.returnType(length, rtts.int); } } ```
### Runtime type introspection ```js class MyClass { constructor(name: string, list: Array) {} } ``` ```js import * as rtts from 'rtts'; class MyClass { constructor(name, list) { } } MyClass.parameters = [ {is: rtts.string}, {is: Array} ]; ```
### Annotations ```js @Component({selector: 'foo'}) class MyComponent { @Inject() constructor(server: Server) {} } ``` ```js class MyComponent { constructor(server) {} } MyComponent.parameters = [{is:Server}]; MyComponent.annotate = [ new Component({selector: 'foo'}), new Inject() ]; ```

Roadmap

## Runtime checking 1. "It allows **free mixing of existing libraries** which do not have type annotations" - "The **semantics** of types can be **left to the implementor**" - "**Runtime type verification** can be used to verify that the JSON returned from the server is of a valid structure" - "Since the type errors should be caught and resolved before pushing the code to production, **all of the assertions can be stripped** from production builds"
> It is far, far easier to make a correct program fast than it is to make a fast program correct. — Herb Sutter
# Fine ## [https://github.com/gcanti](https://github.com/gcanti) ## [@GiulioCanti](https://twitter.com/GiulioCanti)