The TypeScript Masterclass

A presentation at NG-BE 2021 in December 2021 in Ghent, Belgium by Stefan Baumgartner

Slide 1

Slide 1

The TypeScript Masterclass NG-BE Workshop December 2021 @ddprrt - fettblog.eu - typescript-book.com

Slide 2

Slide 2

@ddprrt

Slide 3

Slide 3

Slide 4

Slide 4

What about you? Experience, Excitement, Expectations

Slide 5

Slide 5

Workshop Agenda ⭐ Part 1: Setting the scene ⭐ Part 2: Deep Dive Type System ⭐ Part 3: Generics and Conditional Types ⭐ Part 4: Advanced Types ⭐ Part 5: Methodologies

Slide 6

Slide 6

Setting the Scene Part 1 @ddprrt - fettblog.eu - typescript-book.com

Slide 7

Slide 7

Type Hierarchy in JavaScript https://exploringjs.com/impatient-js/ch_values.html

Slide 8

Slide 8

Type Hierarchy in TypeScript any / unknown Number String Enum Enum Symbol Boolean Class Unique null undefined never Object Union Array Intersection Tuple Function Conditional

Slide 9

Slide 9

Type Hierarchy in TypeScript any / unknown Number Enum primitive String Symbol Enum Unique Boolean Class null undefined never Object Union Array Intersection Tuple Function Conditional

Slide 10

Slide 10

Type Hierarchy in TypeScript any / unknown Number Enum primitive String Symbol Enum Unique Boolean Class null undefined never Object Union compound Array Intersection Tuple Function Conditional

Slide 11

Slide 11

TypeScript is a superset of JavaScript TS JS

Slide 12

Slide 12

1999 1996 1995 1997 2009 2003 2016 2017 2018 2015 ECMAScript 6 / ES2015

Slide 13

Slide 13

1999 1996 1995 1997 2016 2017 2018 2009 2003 2011 TypeScript’s inception 2015 ECMAScript 6 / ES2015

Slide 14

Slide 14

Anders Hejlsberg

Slide 15

Slide 15

Slide 16

Slide 16

Slide 17

Slide 17

https://channel9.msdn.com/Shows/Going+Deep/Anders-Hejlsberg-and-Lars-Bak-TypeScript-JavaScript-and-Dart

Slide 18

Slide 18

Slide 19

Slide 19

It’s not that JavaScript has no type system. There was just no way to formalize it

Slide 20

Slide 20

It’s not that JavaScript has no type system. There was just no way to formalize it Anders Hejlsberg

Slide 21

Slide 21

Red squiggly lines

Slide 22

Slide 22

⭐ Open Source and Open Development ” Closely track ECMAScript standard # Innovate in type system $ Best of breed tooling ⏬ Continually lower barrier to entry & Community, community, community

Slide 23

Slide 23

⭐ Open Source and Open Development ” Closely track ECMAScript standard # Innovate in type system $ Best of breed tooling ⏬ Continually lower barrier to entry & Community, community, community

Slide 24

Slide 24

⭐ TypeScript IS JavaScript ⭐ Language innovation through ECMAScript ⭐ Type system innovation through use cases ⭐ Tooling as prime citizen Non-goal: Apply a sound or “provably correct” type system. Instead, strike a balance between correctness and productivity.

Slide 25

Slide 25

⛩ Gradual, structural, generic ( Distinct value / type namespaces ) Extensive type inference * Control flow analysis & Object-oriented and functional

Slide 26

Slide 26

Tooling

Slide 27

Slide 27

TypeScript JavaScript

Slide 28

Slide 28

TypeScript Code emitting TS Type System Modern JS BrowserJS

Slide 29

Slide 29

This is a lot easier to handle Type System Modern JS BrowserJS

Slide 30

Slide 30

Slide 31

Slide 31

typescript files type checker compiler .ts, .tsx javascript files .js type definitions .d.ts

Slide 32

Slide 32

typescript extra tool ⭐ Type checking $ Bundling ⭐ Type erasure $ Tree shaking ⭐ Transpilation $ Minifications ⭐ JSX Tranformations ⭐ Module resolution

Slide 33

Slide 33

ambient type declarations .ts .ts .ts .ts .ts .ts Your codebase

Slide 34

Slide 34

ambient type declarations .ts .ts Ambient declarations .ts “window” “Object” .ts .ts .ts Your codebase

Slide 35

Slide 35

Type System Deep Dive Part 2 @ddprrt - fettblog.eu - typescript-book.com

Slide 36

Slide 36

⛩ Gradual, structural, generic ( Distinct value / type namespaces + ) Extensive type inference * Control flow analysis + & Object-oriented and functional

Slide 37

Slide 37

Type namespaces

Slide 38

Slide 38

primitive types number Symbol boolean Object undefined null string

Slide 39

Slide 39

top types any unknown number Symbol boolean Object undefined null string

Slide 40

Slide 40

bottom types never number Symbol boolean Object undefined null string

Slide 41

Slide 41

primitive types number Symbol boolean Object undefined null string

Slide 42

Slide 42

primitive types number boolean undefined null string

Slide 43

Slide 43

number value types … -1 boolean NaN 1000000 6 120.3 true false string … undefined null ‘Hello world’ ‘Baumi’

Slide 44

Slide 44

value types … -1 NaN 1000000 6 120.3 true false … undefined null ‘Hello world’ ‘Baumi’

Slide 45

Slide 45

number value types … -1 boolean NaN 1000000 6 120.3 true false string … undefined null ‘Hello world’ ‘Baumi’

Slide 46

Slide 46

number | string | undefined number … -1 boolean NaN 1000000 6 120.3 true false string … undefined null ‘Hello world’ ‘Baumi’

Slide 47

Slide 47

Intersection types { a: string } { } { b: string } a: string, b: string

Slide 48

Slide 48

Type Hierarchy in TypeScript any / unknown Number String Enum Enum Symbol Boolean Class Unique null undefined never Object Union Array Intersection Tuple Function Conditional

Slide 49

Slide 49

Type Hierarchy in TypeScript any / unknown Number String Enum Enum Symbol Boolean Class Unique Super Object Union Array Intersection Tuple Function Conditional null undefined never Sub

Slide 50

Slide 50

Type Hierarchy in TypeScript any / unknown Top Number String Enum Enum Symbol Boolean Class Unique Super Object Union Array Intersection Tuple Function Conditional null undefined Bottom never Sub

Slide 51

Slide 51

Type Hierarchy in TypeScript any / unknown Number String Enum Enum Symbol Boolean Class Unique Object Union Array Intersection Tuple Function Conditional null String template literal types Variadic tuple types undefined never void

Slide 52

Slide 52

any unknown The Wildcard The Cautious type Type safety is in the developer’s responsibility Type safety is in the compiler’s responsibility

Slide 53

Slide 53

unknown any absorbs everything In an intersection everything absorbs unknown type T00 = unknown & null; /” null type T01 = unknown & undefined; /” undefined type T02 = unknown & null & undefined; /” null & undefined (which becomes never) type T03 = unknown & string; /” string type T04 = unknown & string[]; /” string[] type T05 = unknown & unknown; /” unknown type T06 = unknown & any; /” any In a union an unknown absorbs everything type T10 = unknown | null; /” unknown type T11 = unknown | undefined; /” unknown type T12 = unknown | null | undefined; /” unknown type T13 = unknown | string; /” unknown type T14 = unknown | string[]; /” unknown type T15 = unknown | unknown; /” unknown type T16 = unknown | any; /” any

Slide 54

Slide 54

Dealing with any noImplicitAny: Make every any explicit. Easier to scan and find Type assertion: as any can deactivate type safety on short time The as keyword helps you to scan and find situations like this When in doubt: use unknown

Slide 55

Slide 55

Array Tuple variable length fixed element type* fixed length variable element type Each element has the same type * which can be broad! Each position has a defined type Good for collections Good for destructuring + renaming

Slide 56

Slide 56

Arrays vs Tuples const person = [39, “Stefan”]; const [age, name] = person; const person: [number, string] = [39, “Stefan”]; const [age, name] = person;

Slide 57

Slide 57

Arrays vs Tuples const person = [39, “Stefan”]; person: (string | number)[] const [age, name] = person; const person: [number, string] = [39, “Stefan”]; const [age, name] = person;

Slide 58

Slide 58

Arrays vs Tuples const person = [39, “Stefan”]; const [age, name] = person; person: (string | number)[] age: string | number, name: string | number const person: [number, string] = [39, “Stefan”]; const [age, name] = person;

Slide 59

Slide 59

Arrays vs Tuples const person = [39, “Stefan”]; const [age, name] = person; person: (string | number)[] age: string | number, name: string | number const person: [number, string] = [39, “Stefan”]; const [age, name] = person; person: [number, string]

Slide 60

Slide 60

Arrays vs Tuples const person = [39, “Stefan”]; const [age, name] = person; person: (string | number)[] age: string | number, name: string | number const person: [number, string] = [39, “Stefan”]; const [age, name] = person; age: number, name: string person: [number, string]

Slide 61

Slide 61

Arrays vs Tuples const person = [39, “Stefan”]; const [age, name] = person; person: (string | number)[] age: string | number, name: string | number const person: [number, string] = [39, “Stefan”]; const [age, name] = person; age: number, name: string const person = [39, “Stefan”] as const; person: [number, string]

Slide 62

Slide 62

Never The never type is for situations that can never happen This means that you end up in a situation where TypeScript thinks your program shouldn’t be reachable E.g. after throwing an error, when exhausting all options in a union Never is really handy for complex types

Slide 63

Slide 63

Control flow analysis

Slide 64

Slide 64

Control flow analysis Control flow happens on conditions and their branches if-statements / switch-case statements TypeScript can narrow down a type based on the position of a value in the control flow You can help narrowing by typeof, instanceof, “prop” in checks Type predicates help you with elaborate conditions

Slide 65

Slide 65

Control flow analysis Narrowing • Using typeof guards • Using instance of guards • Truthiness (excludes null and undefined) • Discriminated unions • Type predicates

Slide 66

Slide 66

Control flow analysis Typeof function padLeft(padding: number | string, input: string) { if (typeof padding ==% “number”) { return ” “.repeat(padding) + input; padding: number } return padding + input; padding: string }

Slide 67

Slide 67

Control flow analysis Assignments function example() { let x: string | number | boolean; x = Math.random() < 0.5; console.log(x); if (Math.random() < 0.5) { x = “hello”; console.log(x); } else { x = 100; console.log(x); } return x; }

Slide 68

Slide 68

Control flow analysis Assignments function example() { let x: string | number | boolean; x = Math.random() < 0.5; x: boolean console.log(x); if (Math.random() < 0.5) { x = “hello”; console.log(x); } else { x = 100; console.log(x); } return x; }

Slide 69

Slide 69

Control flow analysis Assignments function example() { let x: string | number | boolean; x = Math.random() < 0.5; x: boolean console.log(x); if (Math.random() < 0.5) { x = “hello”; x: string console.log(x); } else { x = 100; console.log(x); } return x; }

Slide 70

Slide 70

Control flow analysis Assignments function example() { let x: string | number | boolean; x = Math.random() < 0.5; x: boolean console.log(x); if (Math.random() < 0.5) { x = “hello”; x: string console.log(x); } else { x: number x = 100; console.log(x); } return x; }

Slide 71

Slide 71

Control flow analysis Assignments function example() { let x: string | number | boolean; x = Math.random() < 0.5; x: boolean console.log(x); if (Math.random() < 0.5) { x = “hello”; x: string console.log(x); } else { x: number x = 100; console.log(x); } x: number | string return x; }

Slide 72

Slide 72

Control flow analysis Type predicates type Dice = 1 | 2 | 3 | 4 | 5 | 6; function isDice(input: number): input is Dice { return [1, 2, 3, 4, 5, 6].includes(input) } function rollDice(input: number) { if(isDice(input)) { console.log(“Rolling..(“) } } Every function that returns a boolean can change the type of a value

Slide 73

Slide 73

Control flow analysis Type predicates type Dice = 1 | 2 | 3 | 4 | 5 | 6; function isDice(input: number): input is Dice { return [1, 2, 3, 4, 5, 6].includes(input) } function rollDice(input: number) { if(isDice(input)) { console.log(“Rolling..(“) } } input: number Every function that returns a boolean can change the type of a value

Slide 74

Slide 74

Control flow analysis Type predicates type Dice = 1 | 2 | 3 | 4 | 5 | 6; function isDice(input: number): input is Dice { return [1, 2, 3, 4, 5, 6].includes(input) } input: number function rollDice(input: number) { if(isDice(input)) { input: Dice console.log(“Rolling..(“) } } Every function that returns a boolean can change the type of a value

Slide 75

Slide 75

Unions Intersections Unions widen the set of possible values Intersections narrow the set of possible values Unions of objects can allow for more values than defined Similar to extends in interfaces Discriminated unions help It’s possible to narrow to never

Slide 76

Slide 76

Discriminated Union types Circle Square without kind type Shape = | { kind: “circle”; radius: number } | { kind: “square”; x: number } | { kind: “triangle”; x: number; y: number }; function area(s: Shape) { if (s.kind ==% “circle”) { return Math.PI * s.radius * s.radius; } else if (s.kind ==% “square”) { return s.x * s.x; } else { return (s.x * s.y) / 2; } } Triangle Circle Square with kind Triangle By setting specific properties to literal types (value types), we can specifically pin point to a subset of a union

Slide 77

Slide 77

Structural typing

Slide 78

Slide 78

nominal structural The name is important The shape is important Type User from lib A and type Person from lib B can look the same, but are incompatible Even with no relation at all types are compatible as long as the structure is compatible Subtypes are allowed

Slide 79

Slide 79

Why is TypeScript structurally typed? • A structural type system is the most fitting way to describe dynamically typed language scenarios without too much overhead • JavaScript relies a lot on object literals. • Graduality: There is still a way to use TypeScript without a single type annotation, just pure JavaScript • There have been approaches to add nominal types. There are even some nominal types hidden within TypeScript

Slide 80

Slide 80

interface type Contract to the outside Contract in your codebase Can be modified from users Allows for advanced type system features

Slide 81

Slide 81

Example: Union and Intersection types Toy Shop • In this example, we model data for a possible toy shop • We define a set of features every Toy should have • We create specialized versions of each Toy • We used literal types (value types) to create discriminated unions • Create a printToy function so we can see what

Slide 82

Slide 82

Task: Union and Intersection Types A Serverless function • Define the interface to a serverless function handler • It can handle three types: • HTTP requests, Queue events, Time based events • Their events have some common properties • Look at the interfaces in the example and try to create a type based model • Do we need kind properties or are we discriminated enough? https://tsplay.dev/w14QYW

Slide 83

Slide 83

Generics & Conditional Types Part 3 @ddprrt - fettblog.eu - typescript-book.com

Slide 84

Slide 84

Generics

Slide 85

Slide 85

Generics Generic type parameters function isAvailable<Formats>( obj: Formats, key: string | number | symbol ): key is keyof Formats { return key in obj; } function isFormatVailable( obj: VideoFormatURLs, key: string ): key is keyof VideoFormatURLs { return key in obj; } function isSubtitleAvailable( obj: SubtitleURLs, key: string ): key is keyof SubtitleURLs { return key in obj; } We call everything within <> generic type parameters Both functions to the same and have a similar signature. We just implement both for the sake of types

Slide 86

Slide 86

Generics Generic annotations vs inference Take a function function identity<T>(arg: T): T { return arg; } what’s the difference between const z = identity<string>(“TypeScript”); and const y = identity(“TypeScript”); Generics can be annotated or inferred, just like regular types Inferred generic types are narrowed to a literal type —> helpful in many scenarios

Slide 87

Slide 87

Generics Generic type constraints function isAvailable<FormatList extends object>( obj: FormatList, key: string ): key is keyof FormatList { return key in obj; } We can limited the possible shapes a generic type can take by applying constraints or “boundaries”

Slide 88

Slide 88

Generics Related type parameters type URLList = { [k: string]: URL; }; function loadFile<Formats extends URLList>(fileFormats: Formats, format: keyof Formats) { /” The real work ahead } We can create relations between generic type parameters to allow for more explicit binding function loadFile2<Formats extends URLList, Keys extends keyof Formats>( fileFormats: Formats, format: Keys ) { /” The real work ahead }

Slide 89

Slide 89

Conditional types

Slide 90

Slide 90

Function overloads vs Conditional types

Slide 91

Slide 91

Advanced Types Part 4 @ddprrt - fettblog.eu - typescript-book.com

Slide 92

Slide 92

Promises and async/await

Slide 93

Slide 93

String template literal types

Slide 94

Slide 94

Task: String Template Literal Types A Format function • Create types for a format function • The format function expects a string with placeholders. • Placeholders are denoted with curly braces, e.g {placeholder} • The second argument is an object with the correct placeholder keys • Bonus • Add optional primitive type parameters, e.g {placeholder:number} • Escape curlies • We only need types, you don’t need to implement the function https://tsplay.dev/mx6zxw

Slide 95

Slide 95

Variadic Tuple Types

Slide 96

Slide 96

Task: Variadic Tuple Types Promisify • Create a promisify function • It accepts a function of any shape, as long as the last argument is a callback function • It returns a function that has the same shape without the last argument • If you call the returning function, you get a Promise that eventually resolves to the original callback’s https://tsplay.dev/WzLBkN

Slide 97

Slide 97

Methodologies & Gotchas Part 5 @ddprrt - fettblog.eu - typescript-book.com

Slide 98

Slide 98

Low maintenance types

Slide 99

Slide 99

Accumulator technique

Slide 100

Slide 100

Void

Slide 101

Slide 101

The problem with Enums

Slide 102

Slide 102

Error Handling Gotchas

Slide 103

Slide 103

Unexpected Intersections

Slide 104

Slide 104

Thank you.