TypeScript

The future today! and more...

What is TypeScript?

CoffeeScript 
Dart 
Haxe 
Opa 

A(nother)  language that compiles to JavaScript 

Superset?

Optional Type System!

Java Types

Crazy OOP diagrams

Aren't we already using Types?

Angular docs

React docs

HTMLInputElement on MDN

So what?!

Refactor

List parameters

Find occurrences

Go to definition

Code completion

inline errors

"JavaScript - the bad parts"

var one = 1;
var two = '2';

var three = one + two;
console.log(three);

"12"

false.toString();

"false"

[1,2,3].toString();

"1,2,3"

"2".toString();

"2"

2.toString();

Unexpected token ILLEGAL

02.toString();

"2"

2 .toString();

"2"

var settings = {
  maxTax : 10
}

var product = {
  price     : 100,
  tax       : settings.maxTax/2,
  discount  : 20
};

var totalPrice = product.price + product.maxTax;
var afterDiscount = totalPrice - product.discount;
console.log(afterDiscount);

NaN

Basic Types

Basic Types

var x: string;
var y: number;
var z: boolean;
var foo: any;
var bar; // Same as "any"
var x;
var y;
var z;
var foo;
var bar;

Arrays


var a: [];
var b: string[];
var p: Product[];

var a;
var b;
var p;
function calcPrice(products: Products[]):number {
  .
  .
  .
}
function calcPrice(products) {
  .
  .
  .
}

Functions as types


var func : (name:string) => number;
function process(x: () => string){
    x().toLowerCase();
}

var func;
function process(x){
    x().toLowerCase();
}

questions : string[];

Interfaces

Structural types

function process(x: {a:string; b:number}){
   return x.a.length;
}
interface IThing {
  a: number;
  b: string;
}

function process(x: IThing){
  return x.a.length;
}

Interfaces

function process(x){
   return x.a.length;
}





function process(x){
  return x.a.length;
}

Structural vs Interface

interface IThing {
  a: number;
  b: string;
}

function process(x: IThing){
  return x.b.length;
}

var n = process({a:10, b:'foo', c:'Extra field'});





function process(x){
  return x.b.length;
}

var n = process({a:10, b:'foo', c:'Extra field'});

Optional fields

interface IPerson {
  age: number;
  name: string;
  address?: string;  // <-- optional field
}

function getName(p: IPerson){
  return p.name;
}

var name = getName({age:10, name:'Me'});

Function fields


interface IPerson {
  age: number;
  name: string;
  address: string;
  walk(distance:number): number;  // <-- a Function
}

questions : IQuestion[];

Function overloads*


interface IPerson {
  age: number;
  name: string;
  address: string;
  walk(distance:number): number;
  walk(destination:string): number;
  walk(location:{x:number, y:number}): number;
}

As a Function (Hybrid)*


interface IPerson {
  age: number;
  name: string;
  address: string;
  walk(distance:number): number;
  (): string;  // <-- a Function
}

new(able)*


interface IHumanStatic {
  MAX_AGE:number;
  new():IHuman;
}

array*


interface StringArray {
  [index: number]: string;
}

Type inference

Where inference takes over?

var x = 3; // x is a number
class MyClass {
  name = "Foo";  // name is a string
}
function foo(value = false) {  // value is a boolean
}
function calc() { 
  return 55;  // calc returns a number
} 

var x = calc(); // x is also a number
if (typeof x === "string") {
  // x is a string
}

backward inference


interface IHuman {
  age: number;
  walk(distance:number):void;
}

var man : IHuman = {
  age : 120,
  walk: function(distance) { 
    console.log(distance);  // distance inferred to be a number
  }
}

backward inference #2


window.onmousedown = function(mouseEvent) { 
  // mouseEvent inferred as MouseEvent
  console.log(mouseEvent.button); 
};

Inference can cause errors

var x = 3; // x is a number
x = "45";  // compiler error
var foo = {};
foo.description = 'I am FOO'; // compiler error
var person : IPersonModel = {};  // compiler error
var x : any = 3; // x can be anything
x = "45";
var foo : any = {};
foo.description = 'I am FOO'; // compiler is happy
var person : IPersonModel = <IPersonModel>{};

Best common type

var x = [0, 1, null];  // x is number[]
var y = [0, 1, 'str']; // y is (string | number)[]

any

var x; // x is any forever
x = '45';  // x is still any
function process(x): {  // x is any
  return x+x*3;         // return type is any
}

process(42); // this does not change the type of x

function getQuestions() : IQuestion[] {}

Type Guards

Type Guards

var x: serverAPI.giveMeXPlease();
if(typeof x === 'string') {
   console.log(x.subtr(1)); // Error
}

// x is still any here
x.unknown(); // OK

instanceof

class Dog { woof() { } }
class Cat { meow() { } }

var pet = serverAPI.getPet();
if(pet instanceof Dog) {
   pet.woof(); // OK
} else {
   pet.woof(); // Error
}

User defined type guards

interface Animal {name: string; }
interface Cat extends Animal { meow(); }

function isCat(a: Animal): a is Cat {
  return a.name === 'kitty';
}

var x: Animal;

if(isCat(x)) {
  x.meow(); // OK, x is Cat in this block
}

Ambient Types

Example for ambient type


declare var angular : any;

lib.d.ts a gift from TypeScript

  • 17K lines of ambient declarations

    • eval, parseInt, encodeURI

    • Math, Date, RegExp

    • Full DOM declarations

    • And many more...

DefinitelyTyped a gift from the comunity

  • 1458 contributors

  • 15K commits

  • Thousands of definition files

  • node.d.ts

  • A word about tsd

Define declarations to existing js code

interface IMemberDetails {
    name:string;
    email:string;
    id:string;
    owner:boolean;
}

interface IWixStatic {
    addEventListener(eventName:string, cb:Function):void;
    closeWindow(message:Object):void; 
    currentMember(cb:(member:IMemberDetails)=>void):void;
}


declare var Wix : IWixStatic;

Extending a declaration*


declare module jasmine {
    interface Matchers {
        addMatchers(expected?: any): boolean;
        toHaveBeenCalledOnce(): boolean;
        toBeOneOf(expected?: any): boolean;
        toHaveClass(expected?: any): boolean;
        toBeDisabled(): boolean;
        toMatchBiUrl(expected: any): boolean;
    }
}

declare var questions : IQuestion[];

DTOs

&

Domain objects*


interface IOrderItemDTO {
  title        : string;
  SKU          : string;
  option       : string[];
  price        : number;
  quantity     : number;
  total        : number;
  image        : string;
}

interface IOrderDTO {
  id              : string;
  items           : IOrderItemDTO[];
  createdDate     : number;
  quantity        : number;
  billingAddress  : IAddressDTO;
  shippingAddress : IAddressDTO;
  buyerNote?      : string;
  currency        : string;
}

Describe your DTOs as interfaces


class Order {
  serialize(orderDTO:IOrderDTO) {
  }

  deserialize():IOrderDTO {
  }
}

Domain objects


class Order implements IOrderDTO {

  constructor(sourceOrderDTO:IOrderDTO) {
    // copy to local variables
  }

}

Domain objects (option #2)

interface IQuestionDTO {

  askedTime : number;

  askedBy: Attendee;

  answers? : boolean;

}

Getters / Setters*

Under the hood

class SettingsData {
  private m_padding : string;
	
  get padding():string {
    return this.m_padding;
  }
	
  set padding(value:string) {
    this.m_padding = value;
  }
}
var SettingsData = (function () {
    function SettingsData() {
    }
    Object.defineProperty(SettingsData.prototype, "padding", {
        get: function () {
            return this.m_padding;
        },
        set: function (value) {
            this.m_padding = value;
        },
        enumerable: true,
        configurable: true
    });
    return SettingsData;
})();

Practical example

class SettingsData {
  private m_padding : string;
  private m_WixSDK  : IWixSDK;

  private sendToSdk(paramName:string, paramValue:any) {
    this.m_WixSDK.setSettings(paramName, paramValue);
  }
	
  get padding():string {
    return this.m_padding;
  }
	
  set padding(value:string) {
    if (value !== this.m_padding) {
      this.m_padding = value;
      this.sendToSdk('padding', this.m_padding);
    }
  }
}

Practical example #2

class ReadOnlyData {
  private m_id : number;

  constructor() {
    this.m_id = 0xF23256234;
  }

  get id():string {
    return this.m_id ;
  }
}

Enums*

Under the hood

enum LogType {None, Warning, Error, Fatal}

function log(message:string, logType:LogType) {
}
var LogType;
(function (LogType) {
    LogType[LogType["None"] = 0] = "None";
    LogType[LogType["Warning"] = 1] = "Warning";
    LogType[LogType["Error"] = 2] = "Error";
    LogType[LogType["Fatal"] = 3] = "Fatal";
})(LogType || (LogType = {}));
function log(message, logType) {
}
var LogType = {
  0: "None",
  1: "Warning",
  2: "Error",
  3: "Fatal",
  "None": 0,
  "Warning": 1,
  "Error": 2,
  "Fatal": 3
}

Control the id


enum LogType {None = 1000, Warning, Error, Fatal}

var logTypeStr = LogType[1000];              // "None"
var logTypeId : LogType = LogType["None"];   // 1000

string id


enum LogType {
  None    = <any>"LOG_TYPE_NONE",
  Warning = <any>"LOG_TYPE_WARNING",
  Error   = <any>"LOG_TYPE_ERROR",
  Fatal   = <any>"LOG_TYPE_FATAL" 
}

var LogType = {
  "LOG_TYPE_NONE": "None",
  "LOG_TYPE_WARNING": "Warning",
  "LOG_TYPE_ERROR": "Error",
  "LOG_TYPE_FATAL": "Fatal",
  "None": "LOG_TYPE_NONE",
  "Warning": "LOG_TYPE_WARNING",
  "Error": "LOG_TYPE_ERROR",
  "Fatal": "LOG_TYPE_FATAL"
}

Decorators*

  • Decorators are called when the class is declared

  • Multiple decorators can be defined

  • Decorators are not allowed on constructors

  • Don't use arrow function inside the decorator

Where can I decorate?

@MyClassDecorator
class Animal {

  @MyPropertyDecorator
  name : string;

  @MyMethodDecorator
  func1() {
  }

  func2(@MyParameterDecorator myParameter: string) {
  }
}

Example: Log


function log(target, propertyKey, descriptor) {
  // save a reference to the original method
  var originalMethod = descriptor.value;

  // override with our own implementation
  descriptor.value = function(...args: any[]) {
      console.log("The method args are: " + JSON.stringify(args));
      var result = originalMethod.apply(this, args);              
      console.log("The return value is: " + result);              
      return result;                                              
  };

  return descriptor;
}

class Foo {
  @log
  func1() {
  }
}

Example: Deprecated


function deprecated(target, name, descriptor) {
  var original = descriptor.value;

  descriptor.value = function() {
    console.warn(`Function ${name} is deprecated.`);
    return original.apply(target, arguments);
  };

  return descriptor;
}


class Foo {
  @deprecated()
  func1() {
  }
}

Example: WixSdk

class ProductWidgetStyleModel {
  @sdkColor('nameColor', 0xFFFFFF)
  nameColor;

  @sdkFont('nameFont', 'Arial')
  nameFont;

  @sdkColor('dividerColor', 0xFF0000)
  dividerColor;

  @sdkUnit('dividerWidth', 20, 0, 245)
  dividerWidth;
  .
  .
  .
}

Things I didn't talk about

var questions;

Tools

  • grunt-ts

  • tsd

  • tslint

  • reference.ts

  • tsconfig.json*

Builders*

  • TBD:

    • The patterns

    • How we use it

Angular1.x

&

TypeScript

Dos and Don'ts

  • Avoid $scope

  • Controller as

  • Use classes

  • Component based structure


function MyDirective():angular.IDirective {
  function link() {
    // link implementation
  }

  return {
    restrict: 'E',
    replace: true,
    templateUrl: '<h1>I am a directive</h1>',
    controller: MyDirectiveController,
    link: link
  }
}

Directive

  • No inheritance

  • No this, in the link function

Factory

interface IProduct {
    name : string;
    id : string;
}

var deferred : ng.IDeferred<IProduct> = $q.defer<IProduct>();
var p : ng.IPromise<IProduct> = deferred.promise;
p.then((product:IProduct) => { console.log(product.name)});

Promises

interface IProduct {
    name : string;
    id : string;
}

var deferred = $q.defer();
var p = deferred.promise;
p.then((product:IProduct) => { console.log(product.name)});
interface IProduct {
    name : string;
    id : string;
}

var deferred = $q.defer<IProduct>();
var p = deferred.promise;
p.then((product) => { console.log(product.name)});
interface IProduct {
    name : string;
    id : string;
}

function loadProducts():ng.IPromise<IProduct[]> {
}

loadProducts.then((products) => { console.log(products)});
Made with Slides.com