The Lowdown on​ TypeScript

Sorin Peste

Technical Evangelist, Microsoft

sorin.peste@microsoft.com

Sorin Peste

Technical Evangelist, Microsoft

sorin.peste@microsoft.com

  • Developer, Architect, Consultant, Evangelist
  • ​Mostly backend development
  • 15 years on C# and .NET Framework
  • TypeScript for REST APIs with Node.js and Express.js

Microsoft      Node.js

• Visual Studio Developer Essentials
Visual Studio Code
• Node.js Tools for Visual Studio
TypeScript

Azure CLI
Azure SDK for Node.js
Azure Mobile Apps SDK
• Application Insights
Node-Chakra and Windows IoT
Visual Studio Online
• Docker Tools, yo docker
• Node.js Technical Steering Committee and Node.js Foundation Board
• and more

TypeScript

  • Superset of ECMAScript 2015 (ES6)
  • Open Source
  • Anders Hejlsberg @ Microsoft, 2012

TypeScript

  • ​Transcompiles to JavaScript
  • tsc Command line options
  • or tsconfig.json
{
    "compilerOptions": {
        "outDir": "./built",
        "allowJs": true,
        "target": "es6"
    },
  "exclude": [
    "node_modules",
    "wwwroot"
  ]
}
> tsc --allowjs=true --outdir="./built" 
--target=es6 filename.ts

Function scoping: var

function f(shouldInitialize: boolean) {
    if (shouldInitialize) {
        var x = 10;
    }

    return x;
}

f(true);  // returns '10'
f(false); // returns 'undefined'

Block scoping: let

function f(shouldInitialize: boolean) {
    if (shouldInitialize) {
        let x = 10;
    }

    return x; // compilation error
}

Block scoping: let

function letTest() {
  let x = 1;

  if (true) {
    let x = 2;  // different variable
    console.log(x);  // 2
  }

  console.log(x);  // 1
}

Block scoping: const

const numLivesForCat = 9;

numLivesForCat--; // compilation error

Basic Types

let isDone: boolean = false;

let decimal: number = 6;
let hex: number = 0xf00d;

let implicitnum = 34; // type inferred as number

isDone = 1;  // compilation error
implicitnum = false; // compilation error

boolean, number

Basic Types

let color: string = "blue";

let fullName: string = `Sorin Peste`;
let age: number = 25;
let sentence: string = `Hello, my name is ${ fullName }.

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

sentence = age; // compilation error


strings

Basic Types

let list1: number[] = [1, 2, 3];

let list2: string[] = ["Amy", "Joe", "Ben"];

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

let list4: Array<string> = ["Amy", "Joe", "Ben"];



let list5: number[] = ["Amy", "Joe", "Ben"]; // compilation error

arrays

Basic Types

enum Color {
    Red = 0,
    Green,
    Blue
};

let c: Color = Color.Green;

let num: number = Color.Blue; // 2

enums

Basic Types

let notSure: any = 4;

notSure.ifItExists(); // okay, ifItExists might exist at runtime
notSure.toFixed(); // okay, toFixed exists (but the compiler doesn't check)

let prettySure: Object = 4;
prettySure.toString(); // Okay
prettySure.toFixed(); // Error: Property 'toFixed' doesn't exist on type 'Object'.

Object, any

Basic Types

function warnUser(): void 
{
    alert("This is my warning message");
}

void

Basic Types

let x : string;
alert(x); // undefined

x = null;
alert(x); // null

null, undefined

{
    "compilerOptions": {
        "strictNullChecks": true
    }
}

Basic Types

// Compiled with --strictNullChecks

let x: number;
x = 1;  
x = undefined;  // Error
x = null;  // Error

let y: number | undefined;
y = 1;  
y = undefined;  
y = null;  // Error

let z: number | null | undefined;
z = 1;  
z = undefined;  
z = null;  

non-nullable types

Basic Types

function showMe(x?: string) : void // x is string | undefined
{
    if (typeof(x) === "string") {
        alert(x.toString()); // x is string
    }
    else {
        alert('x is undefined!');
    }
}

showMe();
showMe("sorin");

type guards

Basic Types

let someValue: any = "this is a string";

let strLength: number = (<string>someValue).length;

let strLength: number = (someValue as string).length;

type assertions

Basic Types

type TextType = string | string[];

let str: TextType = "hello world 2016";

str = ["hello", "world", "2016"];

type aliases

Functions

function add(x, y) {
    return x + y;
}

typing

function add(x: number, y: number): number {
    return x + y;
}

Functions

let myAdd = function(x, y) { return x+y; };

let myAdd = (x, y) => x+y;

typing

let myAdd = function(x: number, y: number): number { return x+y;};

let myAdd = (x: number, y: number) : number => {return x+y;};

let myAdd = (x: number, y: number) => x+y;

Functions

optional params

function buildName(firstName: string, lastName?: string) {
    return lastName ? `{firstName} {lastName}` : `{firstName}`
}

let result1 = buildName("Bob");          // Bob       
let result2 = buildName("Bob", "Adams"); // Bob Adams

Functions

default params

function buildName(firstName: string, lastName: string = "Smith") {
    return `{firstName} {lastName}`;
}

let result1 = buildName("Bob");          // Bob Smith     
let result2 = buildName("Bob", "Adams"); // Bob Adams

Functions

rest params

function buildName(firstName: string, ...restOfName: string[]) {
    return firstName + " " + restOfName.join(" ");
}

let employeeName = buildName("Joseph");

let employeeName = buildName("Joseph", "Samuel", "Lucas", "Jack");

Readability

function findNextMove(game, player, moveType, depth, prune) {
    // implementation
}

[ree-duh-bil-i-tee] ​, noun

function findNextMove(game: Game, player: Player,
    moveType: MoveType, depth: number, prune: boolean = true) 
        : Move {
    // implementation
}

Always code as if the person who ends up maintaining your code is a violent psychopath who knows where you live.​

 

-- anonymous

Classes

class Animal {
    name: string;

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

    move(distanceInMeters: number = 0) {
        console.log(`${this.name} moved ${distanceInMeters}m.`);
    }
}

let ned = new Animal("Ned the generic animal");

ned.move(10);

/* Output:
Ned the generic animal moved 10m.
*/

the basics

Classes

class Snake extends Animal {

    constructor(name: string) { super(name); }
    
    public move(distanceInMeters = 5) {
        console.log("Slithering...");
        super.move(distanceInMeters);
    }
}


let sam = new Snake("Sammy the Python");

sam.move();

/* Output:
Slithering...
Sammy the Python moved 5m.
*/

inheritance

Classes

class Animal2 {
    private name: string;
    public title: string;
    protected furColor: Color;
    
    constructor(theName: string) { this.name = theName; }
}

new Animal2("Cat").name; // Error: 'name' is private
new Animal2("Cat").furColor; // Error: 'furColor' is protected
new Animal2("Cat").title; // OK: 'name' is public

private, protected, public

Classes

class Animal3 {
    private _privName: string;

    get name() : string {
        return this._privName;
    }

    set name(newName: string) {
        this._privName = newName;
    }
}

new Animal3().name; 
new Animal3().name = "Ben"; 
new Animal3()._privName = "Ben"; // Error: "_privName" is private

get, set

Classes

class Animal4 {
    public static maximumAge: number = 35;
}

let age = Animal4.maximumAge; // 35

static

Interfaces

class Calculator {

    add(x: number, y: number) : number {
        console.log(`Adding {x} and {y}.`);        
        return x+y;
    }
}

basics

Interfaces

class ConsoleLog {
    public LogMessage(message: string) : void {
        console.log(message);
    }
}

class Calculator {
    private log: ConsoleLog;

    constructor(log: ConsoleLog) {
        this.log = log;
    }

    add(x: number, y: number) : number {
        this.log.LogMessage(`Adding {x} and {y}.`);
        return x+y;
    }
}

basics

Interfaces

interface ILog {
    LogMessage(message: string) : void;
}

class ConsoleLog implements ILog {
    public LogMessage(message: string) : void {
        console.log(message);
    }
}

class Calculator {
    private log: ILog;

    constructor(log: ILog) {
        this.log = log;
    }

    add(x: number, y: number) : number {
        this.log.LogMessage(`Adding {x} and {y}.`);
        return x+y;
    }
}

basics

Decorators

function sealed(constructor: Function) {
    Object.seal(constructor);
    Object.seal(constructor.prototype);
}

@sealed
export class Animal6 {
    constructor() {}
    move(distanceInMeters: number) {/* implementation */}
}

basics

let aCat = new Animal6();
(aCat as any).color = "black"; // runtime error

Decorators

import { Component } from '@angular/core';

@Component({
  selector: 'my-component',
  template: '<div>Hello my name is {{name}}.'
})
export class MyComponent {
  constructor() {
    this.name = 'Max'
  }
  sayMyName() {
    console.log('My name is', this.name)
  }
}

in Angular2

Decorators

import { Injectable } from '@angular/core';

@Injectable()
export class Logger {
  log(message: string) : void { /* implement */ }
}

@Injectable()
export class Calculator

  constructor(logger: Logger) {  }

  add(x: number, y: number) : number {
    this.logger.log(`Adding {x} and {y}.`);
  }
}

in Angular2

Modules

// validation.ts
export class ZipValidation {

    public ValidateZip(zip: string) : boolean 
    { return false;}
}

basics

//user.ts
import * as validation from './validation';

let zip = new validation.ZipValidation();
zip.ValidateZip("12345");

Modules

// mylibrary.js

export function greet(greeting) {/* implementation */}

Declaration files

// mylibrary.d.ts

declare function greet(greeting: string): void;

// and host at https://github.com/DefinitelyTyped/

Modules

// import typings from npm package

npm install --save @types/express

Using declarations

// app.ts

import * as express from 'express';

let app = express();
//...

Generics

class Queue {   
    enqueue(value: Object) : void {/*enqueue value */}
    dequeue() : Object {/*dequeue value*/}
}

let q1 = new Queue();

q1.enqueue(5);
q1.enqueue("hello");
q1.enqueue(new Animal("Max"));

basics

Generics

class StringQueue {   
    enqueue(value: string) : void {/*enqueue value */}
    dequeue() : string {/*dequeue value*/}
}

class NumberQueue {   
    enqueue(value: number) : void {/*enqueue value */}
    dequeue() : number {/*dequeue value*/}
}

basics

Generics

class GenericQueue<T> {
    enqueue(value: T) : void {/*enqueue value */}
    dequeue() : T {/*dequeue value*/}
}

let q2 = new GenericQueue<string>();
q2.enqueue("Hello");
q2.enqueue(17); // compilation error

let q3 = new GenericQueue<Animal>();
q3.enqueue(new Animal("Max"));
q3.enqueue("Hello"); // compilation error

basics

Generics

/* https://github.com/basarat/typescript-collections */

import * as Collections from 'typescript-collections';

var dict = new Collections.Dictionary<string, Animal>();

dict.setValue("Max", new Animal("Max"));
dict.setValue("Scott", new Animal("Scott"));

collections

LinkedList, Dictionary, Stack, Queue, Set, Bag, BinaryHeap...​

Async and Await

function delay(milliseconds: number) {
    return new Promise<void>(resolve => {
        setTimeout(resolve, milliseconds);
    });
}

async function dramaticWelcome() {
    console.log("Hello");

    for (let i = 0; i < 3; i++) {
        await delay(500);
        console.log(".");
    }

    console.log("World!");
}

dramaticWelcome();

It's Just JavaScript

class Animal5 {
    name: string;

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

    move(distanceInMeters: number = 0) : string {
        return `${this.name} moved ${distanceInMeters}m.`;
    }
}

let a = new Animal5("Max");
let str1 = a.move(5); // "Max moved 5m."

let move1 = a.move;
let str2 = move1(5); // " moved 5m."

this

TypeScriptLowdown

By Sorin Peşte

TypeScriptLowdown

The Lowdown on TypeScript

  • 764