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
- 781