TypeScript & Observables

Agenda

  • What is TypeScript
  • Why use Typescript?
  • TypeScript Concepts
  • Observables
  • Q&A

What is it?

TypeScript is a free and open source programming language developed and maintained by Microsoft. (I know... I know...) It is a strict superset of JavaScript, and adds optional static typing and class-based object-oriented programming to the language.

TypeScript is a superset of JavaScript

Not the first attempt at "fixing" JavaScript.

Over 250 languages compile down to JavaScript

Compile-to-JavaScript frameworks can generally be categorized into two camps 

  • Those that build on top of JavaScript
     
  • Those that abandon JavaScript completely.

Most frameworks fall into the latter camp

  var items = getItems();
  var goSportsTeam = true;
  var dragonball = 'z';

some-javascript.js

some-typescript.ts

Most pre-existing JavaScript

is already valid TypeScript


  function numberCruncher (numA, numB){
    return numA + numB;
  }

JavaScript Issues

var result = numberCruncher(5, 5);
console.log(result); 
>> 10
result = numberCruncher(5, true);
console.log(result); 
>> 6 
result = numberCruncher(5, 'js4lyfe');
console.log(result); 
>> "5js4lyfe" 
result = numberCruncher(5, {param: true});
console.log(result); 
>> "5[object Object]" 
  function myTypedFunction(paramName : dataType) : returnType {
    // Regular junk here
  }

Typing with TypeScript

  var varName : string = 'Woot!';

Type a variable:

Type a function:

  function trimLength(inputVal : string) : number {
    return inputVal.trim().length;
  }

Types

  • Boolean
  • Number
  • String
  • Array
  • Tuple
  • Enum
  • Any
  • Void
  • *Union

Boolean

let flag: Boolean = false;

flag = 2; //Error: Type "Number" is not assignable to type "Boolean"

Number

let decimal: number = 6;
let hex: number = 0xf00d;
let binary: number = 0b1010;
let octal: number = 0o744;

decimal = "hello" //Error!

String

let name: string = `Gene`;
let age: number = 37;
let sentence: string = `Hello, my name is ${ name }.

I'll be ${ age + 1 } years old next month.`

Array

let numeric: Array<number> = [1, 2, 3];

let strings: Array<string> = [1, 2, 3];

let shapes = Array<Shape> = [new Shape(), new Shape(), new Shape()]

Tuple

// Declare a tuple type
let x: [string, number];
// Initialize it
x = ['hello', 10]; // OK
// Initialize it incorrectly
x = [10, 'hello']; // Error

Tuple types allow you to express an array where the type of a fixed number of elements is known, but need not be the same.

Enum

enum Color {Red, Green, Blue};
let c: Color = Color.Green;

Any

Restores basic JavaScript dynamic typing behavior

let notSure: any = 4;
notSure = "maybe a string instead";
notSure = false; // okay, definitely a boolean

Void


  function initSomething() : void {
    doSomeStuff();
  }
var pointless = initSomething(); // 🍋Compiler Error

A function that returns nothing. 

NOTEDeclaring variables of type void is not useful because you can only assign undefined or null to them:

Union 

let foo : string | boolean;

foo = true;
foo = "true";

foo = 1; //Error!

Classes

class Greeter {
    greeting: string;
    constructor(message: string) {
        this.greeting = message;
    }
    greet() : String {
        return "Hello, " + this.greeting;
    }
}
let greeter : Greeter = new Greeter("world");
class Animal {
    name: string;
    constructor(theName: string) { this.name = theName; }
    move(distanceInMeters: number = 0) {
        console.log(`${this.name} moved ${distanceInMeters}m.`);
    }
}

Inheritence

class Horse extends Animal {
    constructor(name: string) { super(name); }
    move(distanceInMeters = 45) {
        console.log("Galloping...");
        super.move(distanceInMeters);
    }
}
let tom: Animal = new Horse("Tommy the Palomino");

tom.move(34);

Public

Public is the default accessor.

//These are equivalent
class Animal {
    public name: string;
    public constructor(theName: string) { this.name = theName; }
    public move(distanceInMeters: number) {
        console.log(`${this.name} moved ${distanceInMeters}m.`);
    }
}

class Animal {
    name: string;
    constructor(theName: string) { this.name = theName; }
    move(distanceInMeters: number) {
        console.log(`${this.name} moved ${distanceInMeters}m.`);
    }
}

Private

Cannot be used outside the class.

class Animal {
    private name: string;
    constructor(theName: string) { this.name = theName; }
}

new Animal("Cat").name; // Error: 'name' is private;

Protected

Protected acts much like the private modifier with the exception that members declared protected can also be accessed by instances of deriving classes

class Person {
    protected name: string;
    constructor(name: string) { this.name = name; }
}

class Employee extends Person {
    private department: string;

    constructor(name: string, department: string) {
        super(name);
        this.department = department;
    }

    public getElevatorPitch() {
        return `Hello, my name is ${this.name} and I work in ${this.department}.`;
    }
}

let howard = new Employee("Howard", "Sales");
console.log(howard.getElevatorPitch());
console.log(howard.name); // error

Static

class Grid {
    static origin = {x: 0, y: 0};
    calculateDistanceFromOrigin(point: {x: number; y: number;}) {
        let xDist = (point.x - Grid.origin.x);
        let yDist = (point.y - Grid.origin.y);
        return Math.sqrt(xDist * xDist + yDist * yDist) / this.scale;
    }
    constructor (public scale: number) { }
}

let grid1 = new Grid(1.0);  // 1x scale
let grid2 = new Grid(5.0);  // 5x scale

console.log(grid1.calculateDistanceFromOrigin({x: 10, y: 10}));
console.log(grid2.calculateDistanceFromOrigin({x: 10, y: 10}));

Abstract Classes

abstract class Department {
    constructor(public name: string) {}
    printName(): void {
        console.log('Department name: ' + this.name);
    }
    abstract printMeeting(): void; // must be implemented in derived classes
}

class AccountingDepartment extends Department {
    constructor() {
        super('Accounting and Auditing'); // constructors in derived classes must call super()
    }

    printMeeting(): void {
        console.log('The Accounting Department meets each Monday at 10am.');
    }

    generateReports(): void {
        console.log('Generating accounting reports...');
    }
}

let department: Department; // ok to create a reference to an abstract type
department = new Department(); // error: cannot create an instance of an abstract class
department = new AccountingDepartment(); // ok to create and assign a non-abstract subclass
department.printName();
department.printMeeting();
department.generateReports(); // error: method doesn't exist on declared abstract type
interface Shape {
    color: string;
}

Interfaces

Static compile-time analysis for enforcing "Duck Typing"

class Square implements Shape {
    constructor(public color: string){}
}
let s = new Square("blue");
interface SquareConfig {
    color?: string;
    width?: number;
}

function createSquare(config: SquareConfig): {color: string; area: number} {
    let newSquare = {color: "white", area: 100};
    if (config.color) {
        newSquare.color = config.color;
    }
    if (config.width) {
        newSquare.area = config.width * config.width;
    }
    return newSquare;
}

let mySquare = createSquare({color: "black"});

Interfaces (2)

interface Shape {
    color: string;
}

Extending Interfaces

interface Square extends Shape {
    sideLength: number;
}

let square = <Square>{};
square.color = "blue";
square.sideLength = 10;
interface GridDefinition {
	origin : { x: number; y: number; };
	calculateDistanceFromOrigin( point: { x: number; y: number; })
}

//ERROR! 
class Grid implements GridDefinition{
    static origin = {x: 0, y: 0}; //The interface won't see static members
    calculateDistanceFromOrigin(point: {x: number; y: number;}) {
        let xDist = (point.x - Grid.origin.x);
        let yDist = (point.y - Grid.origin.y);
        return Math.sqrt(xDist * xDist + yDist * yDist) / this.scale;
    }
    constructor (public scale: number) { }
}

Interface with Static Properties

Generics

function identity(arg: number): number {
    return arg;
}
function identity(arg: any): any {
    return arg;
}
function identity<T>(arg: T): T {
    return arg;
}
class Collection<T> {
    private _items = [];

    public getItems() { return this._items; }

    public count() { return this._items.length; }

    public add(item: T) { this._items.push(item); }
}

Generic Classes

let numberCollection = new Collection<number>();
numberCollection.add(1); 
numberCollection.add(2);
console.log(numberCollection.getItems());
//[1, 2]
let stringCollection = new Collection<string>();
stringCollection.add("Foo");
stringCollection.add("Bar");
stringCollection.add("Baz");
console.log(stringCollection.getItems());
// ["Foo", "Bar", "Baz"]
function assign<T extends U, U>(target: T, source: U): T {
    for (let id in source) {
        target[id] = source[id];
    }
    return target;
}

let x = { a: 1, b: 2, c: 3, d: 4 };
assign(x, { b: 10, d: 20 }); // { a : 1, b: 10, c: 3, d: 20}
assign(x, { e: 0 });  // Error

Type parameters as constraints

F-Bounded Polymorphism

Namespaces

namespace Utility {
    export function log(msg) {
        console.log(msg);
    }
    export function error(msg) {
        console.error(msg);
    }
}

Utility.log("Call me");
Utility.error("maybe!");

TypeScript!

1.8 is out now!

Observables

A generalized mechanism for push-based notification in JavaScript. 

Demo

Questions?

TypeScript

By nicksterling