Upgrade to Pro — share decks privately, control downloads, hide ads and more …

Introduction to TypeScript

Introduction to TypeScript

Samantha Quiñones

July 14, 2016
Tweet

More Decks by Samantha Quiñones

Other Decks in Technology

Transcript

  1. Samantha Quiñones Principal Software Engineer Code and Community Champion AOL

    Inc. @ieatkillerbees @squinones ɂ http://squinones.github.io ɔ https://joind.in/talk/bedf8
  2. What is Typescript? • Superset of JavaScript (all valid JavaScript

    is valid TypeScript) • Provides features from upcoming ECMAScript standards versions • Adds optional strict typing • Adopted by a number of projects, Angular 2 prominently • Microsoft • http://www.typescriptlang.org
  3. Why use TypeScript? • You want to use features of

    modern EMCA standards now • You want to make your complex JavaScript apps more robust
  4. About You • You are familiar with JavaScript • You

    have not worked extensively with statically typed languages • You are familiar with the build pipeline tools appropriate to your projects or environment (or are willing to talk after the session)
  5. Static vs Dynamic Typing […] strong typing is modestly better

    than weak typing, and […] static typing is also somewhat better than dynamic typing. Ray, B., Posnett, D., Filkov, V., Devanbu, P. T. (2014). A Large Scale Study of Programming Languages and Code Quality in Github
  6. Static Typing function div(a:number, b:number): number { return a/b; }

    console.log(div("a", "b")); function div(a, b) { return a / b; } console.log(div("a", "b")); //# sourceMappingURL=typed.js.map ⊗
  7. Static Typing function div(a:number, b:number): number { return a/b; }

    console.log(div("a", "b")); function div(a, b) { return a / b; } console.log(div("a", "b")); //# sourceMappingURL=typed.js.map ⊗ > $ tsc examples/typed.ts examples/typed.ts(5,17): error TS2345: Argument of type 'string' is not assignable to parameter of type 'number'.
  8. Reasons I Prefer Static Typing • Get more out of

    my IDE (I’m looking at you, JetBrains) • Slightly more verbose code is more explicit and I believe in 
 explicit > implicit • Allows the potential for fewer tedious tests. But testing is still vital.
  9. Basic Types - Numbers let decimal:number = 42; let hexadecimal:number

    = 0x2A; let binary:number = 0b101010; let octal:number = 0o52;
  10. Basic Types - Strings let firstName:string = "Samantha"; let greeting:string

    = `Hello, my name is ${ firstName }!`; let greeting:string = "Hello, my name is " + firstName + "!";
  11. Basic Types - Arrays let names:string[] = ["Joe", "Sally"]; let

    ages:number[] = [28, 32]; let heights:Array<number> = [71, 66];
  12. Basic Types - Tuples let coords:[number, number] = [42, 21];

    let params:[string, number] = ["height", 72];
  13. The “Any” Type let anything:any = "foo"; function combine(a, b)

    { return a + b; } let foo = 42; let bar = "fred"; console.log(combine(foo, bar)); function combine(a:any, b:any):any { return a + b; } let foo:number = 42; let bar:string = "fred"; console.log(combine(foo, bar));
  14. Type Assertions let anyValue: any = "This value is a

    string!"; let stringLength: number = <string>anyValue.length; // angle bracket style let stringLength: number = (anyValue as string).length; // JSX-style
  15. let vs. var function blockscope() { for( let i =

    0; i < 5; i++ ) { console.log(i); } console.log(i); } function executionscope() { for( var i = 0; i < 5; i++ ) { console.log(i) } console.log(i); }
  16. let vs. var for (var i = 0; i <

    10; i++) { setTimeout(function() {console.log(i); }, 100 * i); }
  17. let vs. var for (var i = 0; i <

    10; i++) { setTimeout(function() {console.log(i); }, 100 * i); } > $ node examples/letloop.js 10 10 10 10 10 10 10 10 10 10
  18. let vs. var for (var i = 0; i <

    10; i++) { (function(i) { setTimeout(function() { console.log(i); }, 100 * i); })(i); }
  19. let vs. var for (var i = 0; i <

    10; i++) { (function(i) { setTimeout(function() { console.log(i); }, 100 * i); })(i); } > $ node examples/letloop.js 0 1 2 3 4 5 6 7 8 9
  20. let vs. var for (let i = 0; i <

    10 ; i++) { setTimeout(function() {console.log(i); }, 100 * i); }
  21. let vs. var > $ node examples/letloop.js 0 1 2

    3 4 5 6 7 8 9 for (let i = 0; i < 10 ; i++) { setTimeout(function() {console.log(i); }, 100 * i); }
  22. let vs. var for (let i = 0; i <

    10 ; i++) { setTimeout(function() {console.log(i); }, 100 * i); } var _loop_1 = function(i) { setTimeout(function () { console.log(i); }, 100 * i); }; for (var i = 0; i < 10; i++) { _loop_1(i); }
  23. Constants var foo = 42; function timesFoo(a) { return foo

    * a; } console.log(timesFoo(2)); const foo:number = 42; function timesFoo(a:number): number { return foo * a; } console.log(timesFoo(2));
  24. Constants const foo:number = 42; function timesFoo(a:number): number { return

    foo * a; } foo = 1701; console.log(timesFoo(2)); var foo = 42; function timesFoo(a) { return foo * a; } foo = 1701; console.log(timesFoo(2)); > $ tsc examples/consts.ts examples/consts.ts(8,1): error TS2450: Left- hand side of assignment expression cannot be a constant.
  25. Interfaces interface Meat { isCooked:boolean; sourceAnimal:string; } function cookMeat(meat:Meat): Meat

    { meat.isCooked = true; return meat; } let sirloin:Meat = { isCooked: false, sourceAnimal: "cattle" }; cookMeat(sirloin); console.log(sirloin.isCooked);
  26. Interfaces - Optional Properties interface Meat { isCooked:boolean; isGame:boolean; sourceAnimal:string;

    gaminess?:number; } function cookMeat(meat:Meat):Meat { meat.isCooked = true; return meat; } let sirloin:Meat = { isCooked: false, isGame:false, sourceAnimal: "cattle" }; cookMeat(sirloin); console.log(sirloin.isCooked); let venison:Meat = { isCooked: false, isGame:true, sourceAnimal: "deer", gaminess: 3 }; cookMeat(venison); console.log(venison.isCooked);
  27. Interfaces - Undefined Properties let venison:Meat = { isCooked: false,

    isGame:true, sourceAnimal: "deer", gameiness: 3 }; cookMeat(venison); console.log(venison.isCooked); examples/recipes/meat.ts(23,5): error TS2322: Type '{ isCooked: boolean; isGame: boolean; sourceAnimal: string; gameiness: number; }' is not assignable to type 'Meat'. Object literal may only specify known properties, and 'gameiness' does not exist in type 'Meat'.
  28. Interfaces - Index Signatures interface Meat { isCooked:boolean; isGame:boolean; sourceAnimal:string;

    gaminess?:number; [otherProperty: string]: any; } let ChickenBreast:Meat = { isCooked: false, isGame: false, sourceAnimal: "chicken", skinless: true };
  29. Interfaces - Function Interfaces interface Ingredient { name: string; }

    interface MixedIngredient { ingredients: Ingredient[]; } interface MixFunction { (ingredients:Ingredient[], speed:number): MixedIngredient; } let mixer:MixFunction = function (ingredients:Ingredient[], speed:number): MixedIngredient { return { ingredients: ingredients }; }; let mixed = mixer([{name: "flour"}, {name: "sugar"}], 10); console.log(mixed);
  30. Interfaces - Indexed Types interface Ingredient { name: string; }

    interface IngredientList { [index: number]: Ingredient } interface MixFunction { (ingredients:Ingredient[], speed:number): any; } interface MixFunction2 { (ingredients:IngredientList, speed: number): any; }
  31. Interfaces - Indexed Types interface Ingredient { name: string, quantity:

    number, unit: string } interface RecipeDictionary { [index: string]: Ingredient }
  32. Interfaces - Indexed Types interface Ingredient { name: string, quantity:

    number, unit: string } interface RecipeDictionary { [index: string]: Ingredient name: string; } > $ tsc examples/dictionary.ts examples/dictionary.ts(9,5): error TS2411: Property 'name' of type 'string' is not assignable to string index type 'Ingredient'.
  33. Interface Extension interface Meat { isCooked:boolean; sourceAnimal:string; [otherProperty: string]: any;

    } interface Game extends Meat { gaminess:number; } let GroundChuck:Meat = { isCooked:false, sourceAnimal: "cattle" }; let DuckBreast:Game = { isCooked:false, sourceAnimal: "duck", gaminess: 3 };
  34. Implementing Interfaces interface Mixer { mix:MixFunction, clean(): void } class

    KitchenAid implements Mixer { } > $ tsc examples/implementing-interfaces.ts examples/implementing-interfaces.ts(18,7): error TS2420: Class 'KitchenAid' incorrectly implements interface 'Mixer'. Property 'mix' is missing in type 'KitchenAid'.
  35. Implementing Interfaces interface Mixer { mix:MixFunction, clean(): void } class

    KitchenAid implements Mixer { mix(ingredients:Ingredient[], speed:number): MixedIngredient { return { ingredients: ingredients }; }; clean(): void { // } }
  36. Classes class Ingredient { name:string; unit:string; quantity:number; constructor(name:string, unit:string, quantity:number)

    { this.name = name; this.unit = unit; this.quantity = quantity; } recipeString(): string { return `${this.name} - ${this.quantity} ${this.unit}`; } } let flour = new Ingredient("flour", "cup", 1);
  37. Classes var Ingredient = (function () { function Ingredient(name, unit,

    quantity) { this.name = name; this.unit = unit; this.quantity = quantity; } Ingredient.prototype.recipeString = function () { return this.name + " - " + this.quantity + " " + this.unit; }; return Ingredient; })(); var flour = new Ingredient("flour", "cup", 1);
  38. Inheritance in JavaScript • JavaScript uses prototypal inheritance • Inherited

    properties come from a linked object (the prototype) • The prototype model can be used to implement classical inheritance • ES6 (and TypeScript) introduce a “class” keyword that provides syntactic sugar for creating classical inheritance chains using prototypes
  39. Classes - Inheritance enum FlourType { White, Wheat, Rye, Corn

    } class Flour extends Ingredient { type:FlourType; constructor(name:string, unit:string, quantity:number, type:FlourType) { super(name, unit, quantity); this.type = type; } } let wheatFlour = new Flour("flour", "cup", 1, FlourType.Wheat);
  40. Classes - Inheritance var FlourType; (function (FlourType) { FlourType[FlourType["White"] =

    0] = "White"; FlourType[FlourType["Wheat"] = 1] = "Wheat"; FlourType[FlourType["Rye"] = 2] = "Rye"; FlourType[FlourType["Corn"] = 3] = "Corn"; })(FlourType || (FlourType = {})); var Flour = (function (_super) { __extends(Flour, _super); function Flour(name, unit, quantity, type) { _super.call(this, name, unit, quantity); this.type = type; } return Flour; })(Ingredient); var wheatFlour = new Flour("flour", "cup", 1, FlourType.Wheat); //# sourceMappingURL=inheritance.js.map
  41. Classes - Abstracts abstract class Ingredient { constructor(protected name:string) {}

    abstract printRecipeString(): string; } class OliveOil extends Ingredient { constructor() { super("Olive Oil"); } printRecipeString():string { return `${this.name}`; } }
  42. Classes - Extending class Ingredient { name:string; unit:string; quantity:number; }

    interface Oil extends Ingredient { viscosity:number; }
  43. Classes - Visibility class Ingredient { name:string; unit:string; quantity:number; }

    class Ingredient { public name:string; public unit:string; public quantity:number; }
  44. Classes - Visibility class Ingredient { name:string; unit:string; quantity:number; }

    class Ingredient { public name:string; public unit:string; public quantity:number; } == • Public properties are visible to any caller • Protected properties are visible to a class instance and its children • Private properties are visible only to a class instance
  45. Classes - Private Members class Ingredient { private _name:string; private

    _unit:string; private _quantity:number; constructor(name:string, unit:string, quantity:number) { this._name = name; this._unit = unit; this._quantity = quantity; } } let butter= new Ingredient("butter", "tbsp", 2); butter._unit = "cup"; > $ tsc examples/classes/private.ts examples/classes/private.ts(14,1): error TS2341: Property '_unit' is private and only accessible within class 'Ingredient'.
  46. Classes - Private Members class Ingredient { private _name:string; constructor(name:string)

    { this._name = name; } } class Butter extends Ingredient { constructor() { super(“Butter"); } } class Condiment { private _name:string; constructor(name:string) { this._name = name; } } let ingredients:Ingredient[] = [new Butter(), new Condiment("Ketchup")];
  47. Classes - Private Members class Ingredient { private _name:string; constructor(name:string)

    { this._name = name; } } class Butter extends Ingredient { constructor() { super(“Butter"); } } class Condiment { private _name:string; constructor(name:string) { this._name = name; } } let ingredients:Ingredient[] = [new Butter(), new Condiment("Ketchup")]; > $ tsc examples/classes/private.ts examples/classes/private.ts(23,5): error TS2322: Type '(Butter | Condiment)[]' is not assignable to type 'Ingredient[]'. Type 'Butter | Condiment' is not assignable to type 'Ingredient'. Type 'Condiment' is not assignable to type 'Ingredient'. Types have separate declarations of a private property '_name'.
  48. Classes - Protected Members class Ingredient { private _name:string; private

    _unit:string; constructor(name:string) { this._name = name; } } class Butter extends Ingredient { constructor() { super("Butter"); this._unit = "tbsp"; } } let butter = new Ingredient("butter"); > $ tsc examples/classes/protected.ts examples/classes/protected.ts(12,9): error TS2341: Property '_unit' is private and only accessible within class 'Ingredient'.
  49. Classes - Protected Members > $ tsc examples/classes/protected.ts examples/classes/protected.ts(17,1): error

    TS2445: Property '_unit' is protected and only accessible within class 'Ingredient' and its subclasses. class Ingredient { private _name:string; protected _unit:string; constructor(name:string) { this._name = name; } } class Butter extends Ingredient { constructor() { super("Butter"); this._unit = "tbsp"; } } let butter= new Ingredient("butter"); butter.unit = "cup";
  50. Classes - Static Properties class Ingredient { private grams:number; static

    gramsPerOunce:number = 28.35; getWeightInGrams():number { return this.grams; } } let butter = new Ingredient(); let ounces = butter.getWeightInGrams() * Ingredient.gramsPerOunce;
  51. Classes - Static Properties function Ingredient() {} Ingredient.prototype.foo = function

    () {}; Ingredient.bar = function () {}; Ingredient.bar(); // static property
  52. Functions function namedAdd(a:number, b:number): number { return a+b; } let

    anonymousAdd = function (a:number, b:number): number { return a+b; }; // reference parameter list return type definition let fullAdd: (a:number, b:number) => number = function (a:number, b:number): number { return a+b; };
  53. Functions - Optionals & Defaults function incrementOptional(base:number, incrementBy?:number) { return

    base + incrementBy || 1; } function incrementDefaulted(base:number, incrementBy:number=1): number { return base + incrementBy; }
  54. Functions - Variadic Parameters interface Ingredient {} interface IngredientList {

    ingredients:Ingredient[]; } function createIngredientList(...ingredients:Ingredient[]): IngredientList { let list = { ingredients:[] }; for (let ingredient of ingredients) { list.ingredients.push(ingredient); } return list; }
  55. Functions - Overloading function cook(ingredients:IngredientList, cookingAppliance:any, ...options:Array<any>): Meal { if

    (typeof cookingAppliance === "Skillet") { return new SkilletMeal(ingredients); } else if (typeof cookingAppliance === "Crockpot") { return new CrockpotMeal(ingredients, options[0], options[1]); } }
  56. Functions - Overloading function cook(ingredients:IngredientList, crockpot:Crockpot, temp:number, time:number): CrockpotMeal; function

    cook(ingredients:IngredientList, skillet:Skillet): SkilletMeal; function cook(ingredients:IngredientList, cookingAppliance:any, ...options:Array<any>): Meal { if (typeof cookingAppliance === "Skillet") { return new SkilletMeal(ingredients); } else if (typeof cookingAppliance === "Crockpot") { return new CrockpotMeal(ingredients, options[0], options[1]); } } let skillet = cook(new IngredientList(), new Skillet()); let crockpost = cook(new IngredientList(), new Crockpot(), 225, 360); let dutchoven = cook(new DutchOven()); > $ tsc examples/functions/overload.ts examples/functions/overload.ts(19,17): error TS2346: Supplied parameters do not match any signature of call target.
  57. Lambdas let recipe = { ingredients: [ "flour", "water", "eggs",

    "butter" ], createWriter: function () { return function () { return this.ingredients.join("\n"); } } };
  58. Lambdas let recipe = { ingredients: [ "flour", "water", "eggs",

    "butter" ], createWriter: function () { return () => { return this.ingredients.join("\n"); } } };
  59. Lambdas var recipe = { ingredients: ["flour", "water", "eggs", "butter"],

    createWriter: function () { return function () { return this.ingredients.join("\n"); }; } }; var recipe = { ingredients: ["flour", "water", "eggs", "butter"], createWriter: function () { var _this = this; return function () { return _this.ingredients.join("\n"); }; } };
  60. Generics • Allow flexibility in strictly typed systems • Allows

    type resolution at runtime through the use of generic components
  61. Generics class Recipe { ingredients:Ingredient[]; add(ingredient:Ingredient) { this.ingredients.push(ingredient); } }

    class VeganRecipe extends Recipe { ingredients:VeganIngredient[]; add(ingredient:VeganIngredient) { this.ingredients.push(ingredient); } } let veganRecipe = new VeganRecipe(); veganRecipe.add(new VeganIngredient()); veganRecipe.add(new MeatIngredient()); // error
  62. Generics class Recipe { ingredients:Ingredient[]; add(ingredient:Ingredient) { this.ingredients.push(ingredient); } }

    class VeganRecipe extends Recipe { ingredients:VeganIngredient[]; add(ingredient:VeganIngredient) { this.ingredients.push(ingredient); } } class MeatRecipe extends Recipe { ingredients:MeatIngredient[]; add(ingredient:MeatIngredient) { this.ingredients.push(ingredient); } } let meatRecipe = new MeatRecipe(); meatRecipe.add(new MeatIngredient()); let veganRecipe = new VeganRecipe(); veganRecipe.add(new VeganIngredient()); veganRecipe.add(new MeatIngredient()); // error
  63. Generics class Recipe<T> { ingredients:T[]; add(ingredient:T) { this.ingredients.push(ingredient); } }

    let meatRecipe = new Recipe<MeatIngredient>(); meatRecipe.add(new MeatIngredient()); let veganRecipe = new Recipe<VeganIngredient>(); veganRecipe.add(new VeganIngredient()); veganRecipe.add(new MeatIngredient()); // error
  64. Generics class Recipe<T extends Ingredient> { ingredients:T[]; add(ingredient:T) { this.ingredients.push(ingredient);

    } } let badRecipe = new Recipe<BadIngredient>(); badRecipe.add(new BadIngredient()); > $ tsc examples/generics/generics.ts examples/generics/generics.ts(51,28): error TS2344: Type 'BadIngredient' does not satisfy the constraint 'Ingredient'.
  65. Unions interface Chicken { boned:boolean; pluck(): void; fillet(): Meat; }

    interface Fish { boned:boolean; scale(): void; fillet(): Meat; } function fillet(animal:Chicken|Fish): Meat { return animal.fillet(); }
  66. Unions function fillet(animal:Chicken|Fish): Meat { if (animal.pluck) { animal.pluck(); }

    if (animal.scale) { animal.scale(); } return animal.fillet(); } > $ tsc examples/unions.ts examples/unions.ts(13,16): error TS2339: Property 'pluck' does not exist on type 'Chicken | Fish'. examples/unions.ts(14,16): error TS2339: Property 'pluck' does not exist on type 'Chicken | Fish'. examples/unions.ts(16,16): error TS2339: Property 'scale' does not exist on type 'Chicken | Fish'. examples/unions.ts(17,16): error TS2339: Property 'scale' does not exist on type 'Chicken | Fish'.
  67. Unions function fillet(animal:Chicken|Fish): Meat { if ((<Chicken>animal).pluck) { (<Chicken>animal).pluck(); }

    if ((<Fish>animal).scale) { (<Fish>animal).scale(); } return animal.fillet(); } \
  68. Type Guards & Predicates function isChicken(animal:Chicken|Fish): animal is Chicken {

    return (<Chicken>animal).pluck !== undefined; } function isFish(animal:Chicken|Fish): animal is Fish { return (<Fish>animal).scale !== undefined; } function fillet(animal:Chicken|Fish): Meat { if (isChicken(animal)) { animal.pluck(); } if (isFish(animal)) { animal.scale(); } return animal.fillet(); }
  69. Intersections interface Meat { sourceAnimal:string } interface Game { gaminess:number

    } function prepareGameMeat(meat:Meat&Game): PreparedMeat { return new PreparedMeat(meat); }
  70. Mixins interface Loggable { log(level:string, message:string): void } interface Serializable

    { serialize(): string } class ThingDoer implements Loggable, Serializable{ log(level:string, message:string):void { logTheThing(); } serialize():string { return "serialized ThingDoer!"; } } function doThing(thing:Loggable&Serializable): string { thing.log('info', 'Doing the thing!'); return thing.serialize(); }
  71. Type Aliases type Unit = string; type UnitConverter = (Unit,

    value:number) => number; type WhiteMeat = Fish | Poultry; type Coords = [number,number,number]; type KosherMeat = "cattle" | "goat" | "sheep" | "chicken" | "duck" | "goose" type LinkedList<T> = T & { next: LinkedList<T> }; interface Ingredient { name:string; } var ingredients:LinkedList<Ingredient>; ingredients.next.name; ingredients.next.next.name;
  72. String Types // code copyright Microsoft 2012-2016 type Easing =

    "ease-in" | "ease-out" | "ease-in-out"; class UIElement { animate(dx: number, dy: number, easing: Easing) { if (easing === "ease-in") { // ... } else if (easing === "ease-out") { } else if (easing === "ease-in-out") { } else { // error! should not pass null or undefined. } } } let button = new UIElement(); button.animate(0, 0, "ease-in"); button.animate(0, 0, "uneasy"); // error: "uneasy" is not allowed here
  73. Modules export interface Meat {} export class Beef implements Meat

    {} export class Chicken implements Meat {} import * as meats from './meats'; let chicken = new meats.Chicken();
  74. Namespaces namespace Meats { export interface Meat {} export class

    Beef implements Meat {} export class Chicken implements Meat {} } let chicken = new Meats.Chicken();
  75. Declarations • Allow us to describe the shape of objects

    defined outside a TS app • https://github.com/typings/typings • https://github.com/typings/registry
  76. tsconfig.json { "compilerOptions": { "module": "commonjs", "noImplicitAny": true, "removeComments": true,

    "preserveConstEnums": true, "outDir": "built/", "sourceMap": true }, "exclude": [ "node_modules" ] }
  77. Thank you for listening! Rate speakers & the event! Questions?

    Samantha Quiñones @ieatkillerbees @squinones ɂ http://squinones.github.io ɔ https://joind.in/talk/bedf8