TypeScript

le JavaScript statiquement typé

Qui suis-je ?

  • Benoit Lemoine
  • Développeur full-stack chez Captain Dash
  • @benoit_lemoine

JavaScript en 2015...

  • tout le monde en fait...
  • ...pour coder tout et n'importe quoi

À la mode != Pratique

JavaScript c'est bien, mais...

Javascript en 2015 c'est...

... pleins de gens qui se plaignent de JavaScript

Et un site dédié : wtfjs.com

Transpilers

https://github.com/jashkenas/coffeescript/wiki/list-of-languages-that-compile-to-JS (plus de 350)

TypeScript

  • Made in Microsoft en 2012...
  • ... mais open-source et libre (License Apache 2) : https://github.com/Microsoft/TypeScript
  • Développé par Anders Hejlsberg (Delphi, Turbo Pascal, C#, etc.)
  • Super-ensemble d'ES5
  • Typage statique
  • Polyfill pour ES6

Le système de type

Les types - déclarer un type


var name:string = 'Dolan';
var nbLegs:number = 2;

var isMamal = false;

//doesn't compile
//number is not assignable to type boolean
isMamal = 3;
					 

Les types - les génériques


var featherColors:Array<string> = ['pink', 'lime', 'magenta'];


var lengthOfColors: Array<number> = featherColors.map(function(color) {
	return color.length;
}); // [5, 3, 4]
					

Les types - Les classes


class Animal {
  constructor(public name) { }
}						

class Duck extends Animal {
  quack() {
    return "quack";
  }
}

class Platypus extends Animal {
  static isMamal = true;
}

var dolan:Duck = new Duck("Dolan");
console.log(dolan.quack());
					

Les types - Les enums


enum Planet {
  Mercury, Venus, Earth, Mars, Jupiter, Saturn, Uranus, Neptun
}	

Planet.Mercury != Planet.Venus //true
Planet.Earth == 2 //true et compile :'(

Les types - typage graduel

	
var scrooge = new Duck('Scrooge');
var perry = new Platypus('Perry');
//Doesn't compile Platypus is not assignable to type Duck
scrooge = perry;

//we can assign anything to any
var jokerDuck:any = perry;

//We can assign any to everything
scrooge = jokerDuck;
					

Les types - Les interfaces


interface Quacker {
  name:string
  quack():string;
} 			

class Goose extends Animal implements Quacker {
  quack() {
    return "honk";
  }
}
var daffie:Quacker = new Goose("daffie");
					

Les types - Le typage structurel


interface Quacker {
  name:string
  quack():string;
} 			

var chicken:Quacker = {
  name:'Chicken', 
  quack: function() {
    return 'cluck cluck';
  }
};
					

Les guardes


function isDuck(a:Animal):a is Duck {
  return a.constructor.name == 'Duck';
}

var x: Animal;

if(isDuck(x)) {
  x.quack(); // OK, x is Duck in this block
}

ADT du pauvre


class Nothing {
  isDefined:boolean = false;
}
class Just<A> { 
  constructor(public el:A){}
  isDefined:boolean = true; 
}
type Maybe<A> = Just<A> | Nothing

function isJust<A>(m:Maybe<A>):m is Just<A> { return m.isDefined}

const justStr:Maybe<String> = new Just("maString")
//console.log(justStr.el.length) //ne compile pas
if(isJust(justStr)) {
  console.log(justStr.el.length)
}

 	

Pour les puristes

TypeScript is unsound


var ducks:Array<Duck> = [new Duck('Donald')];
var animals:Array<Animal> = ducks; //Listes covariantes
animals[0] = new Sheep('Dolly'); //listes mutables

ducks.quack(); // undefined is not a function

ES6 - ES7

Déclaration de variable


const myVar = 2;

//ne compile pas
myVar = 3;


if(true) {
  let anotherVar = 2;
}
//ne compile pas	
console.log(anotherVar);

Arrow function


[1,2,3].map( value => value + 1) //vaut [2,3,4]	

class JqueryController {
  public myVar = 2;
  constructor() {
    $('#unElementDuDom').click(function() {
      console.log(this.myVar); //Affiche undefined
    });

    $('#unElementDuDom').click( () => {
      console.log(this.myVar); //Affiche 2
    });
  }
}

String Template


let myVar = 23	
var aString = `	
<div>
  <span>${23}</span>
</div>
`

Autres choses


var add = function(nb1, nb2 = 0) {
  return nb1 + nb2;
}
console.log(add(1,2)) // 3
console.log(add(1)) // 1


var sum = function(...nb:Array<number>) {
  return nb.reduce(add, 0);
}

console.log(sum(1,2,3,4)) // 10

Le cauchemar du Javaïste

Les décorateurs

https://github.com/wycats/javascript-decorators

class Person {
  constructor(private first:string, private  last:string){}
  
  @readonly
  name() { return `${this.first} ${this.last}` }
}

function readonly(target, name, descriptor) {
  descriptor.writable = false;
  return descriptor;
}

Exemple : Angular 2


@Component({selector: 'my-app'})
@View({template: '

Hi {{ name }}

'}) // Component controller class MyAppComponent { constructor() { this.name = 'Howard'; } }

Support de JSX

                	
class MyComponent extends React.Component {
  props: {
    who?: string
  },
  render() {
    return <div>Hello, {this.props.who}</div>; 
  }
}	

Les modules

Module interne

Validation.ts

module Validation {
    export interface StringValidator {
        isAcceptable(s: string): boolean;
    }
}
LettersOnlyValidator.ts

/// <reference path="Validation.ts" />
module Validation {
    var lettersRegexp = /^[A-Za-z]+$/;
    export class LettersOnlyValidator implements StringValidator {
        isAcceptable(s: string) {
            return lettersRegexp.test(s);
        }
    }
}

Module externe

Validation.ts

export interface StringValidator {
    isAcceptable(s: string): boolean;
}
LettersOnlyValidator.ts

import validation = require('./Validation');
var lettersRegexp = /^[A-Za-z]+$/;
export class LettersOnlyValidator implements validation.StringValidator {
    isAcceptable(s: string) {
        return lettersRegexp.test(s);
    }
}

Module ES6

Validation.ts

interface StringValidator {
    isAcceptable(s: string): boolean;
}
export default StringValidator
LettersOnlyValidator.ts

import StringValidator from './Validation';
var lettersRegexp = /^[A-Za-z]+$/;
export class LettersOnlyValidator implements StringValidator {
    isAcceptable(s: string) {
        return lettersRegexp.test(s);
    }
}

Utiliser TypeScript avec le monde extérieur

Les déclarations d'ambiance


declare var _;

_.filter([1,2,3], (i) => i%2 === 0);

Definitely Typed

http://definitelytyped.org/

/// <reference path="lodash/lodash.d.ts" />

_.filter([1, 2, 3, 4], (i) => i%2 === 0) // [2,4]
_.filter([1, 2, 3, 4], (i:string) => i%2 === 0) //ne compile pas

TSD

http://definitelytyped.org/tsd/

TypeScript sur un vrai projet

Liste des prérequis

  • Projet moderne : module ES6
  • mais pas trop non plus : angular 1.x + ES5

JSPM

https://github.com/frankwallis/plugin-typescript

System.config({
  defaultJSExtensions: true,
  transpiler: "typescript",
  typescriptOptions: {
    "experimentalDecorators": true,
    "typeCheck": true
  },
  packages: {
    "javascripts/spa2/src": {
      "defaultExtension": "ts",
      "meta": {
        "*.ts": { "loader": "ts" }
      }
    }
  }
}
	

Du code !


//MyController.ts
/// <reference path="../definitions/angularjs/angular.d.ts" />
class MyController {

   public name:string;

   static $inject = ['$http'];
   constructor($http:ng.IHttpService) {
      $http.get('anUrl').then(response => this.name = response.data);
   }
}

export default MyController

Du code !


//app.ts
/// <reference path="../definitions/angularjs/angular.d.ts" />
import * as angular from 'angular';
import ngAnimate from 'angular-animate';
import MyController from './MyController';

angular.module('myApp', [ngAnimate])
    .controller('MyController', MyController);

Du code !

           
//index.html
 <script src="jspm_packages/system.js"></script>
 <script src="config.js"></script>
 <script>
  System.import('app').then(function() {
    return System.import('angular');
  })
  .then(function(angular) {
    console.log('running');
    angular.bootstrap(document, ['myApp']);
    console.log(JSON.stringify(System.typescriptOptions));
  })
  .catch(function(err) {
     console.log('Error: ', err.stack);
  });
 </script>

Le futur

Typescript 1.7

  • more ES6
  • more ES7

TypeScript x.x - RTTI


//TypeScript

function firstChar(word : string) {
  return word.charAt(0);    							
}


var x : any = 3;
firstChar(x);
						

//JavaScript							
import * as rtts from 'rtts';
							
function firstChar(word) {
  rtts.types(word, rtts.string);
  return word.charAt(0);    							
}

var x = 3;
firstChar(x);
						

Pourquoi choisir Typescript ?

Babel

Angular 2

https://angular.io/

import {Component, View, bootstrap, NgFor} from 'angular2/angular2';
@Component({selector: 'todo-list'})
@View({
  template: `<ul><li *ng-for="#todo of todos">{{ todo }}</li></ul>`,   
  directives: [NgFor]
})
class TodoList {
  todos: Array<string> = ["Eat Breakfast", "Walk Dog", "Breathe"];
  constructor() {    
  }  
}
bootstrap(TodoList);				
			

Aurelia

http://aurelia.io/

export class Welcome{
  heading:string = 'Welcome to the Aurelia Navigation App!';
  firstName:string = 'John';
  lastName:string = 'Doe';

  get fullName(){
    return `${this.firstName} ${this.lastName}`;
  }

  submit(){
    alert(`Welcome, ${this.fullName}!`);
  }
}				
			

Les limites

  • Un transpiler de plus
  • La vitesse de compilation
  • Les fichiers de définitions

Conclusion

  • Migration Progressive
  • Facilité d'apprentissage
  • Compatibilité avec ES6
  • Refactoring simplifié
  • Assistance de l'IDE améliorée

Questions ?