The TypeScript Masterclass NG-BE Workshop December 2021 @ddprrt - fettblog.eu - typescript-book.com
A presentation at NG-BE 2021 in December 2021 in Ghent, Belgium by Stefan Baumgartner
 
                The TypeScript Masterclass NG-BE Workshop December 2021 @ddprrt - fettblog.eu - typescript-book.com
 
                @ddprrt
 
                 
                What about you? Experience, Excitement, Expectations
 
                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
 
                Setting the Scene Part 1 @ddprrt - fettblog.eu - typescript-book.com
 
                Type Hierarchy in JavaScript https://exploringjs.com/impatient-js/ch_values.html
 
                Type Hierarchy in TypeScript any / unknown Number String Enum Enum Symbol Boolean Class Unique null undefined never Object Union Array Intersection Tuple Function Conditional
 
                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
 
                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
 
                TypeScript is a superset of JavaScript TS JS
 
                1999 1996 1995 1997 2009 2003 2016 2017 2018 2015 ECMAScript 6 / ES2015
 
                1999 1996 1995 1997 2016 2017 2018 2009 2003 2011 TypeScript’s inception 2015 ECMAScript 6 / ES2015
 
                Anders Hejlsberg
 
                 
                 
                https://channel9.msdn.com/Shows/Going+Deep/Anders-Hejlsberg-and-Lars-Bak-TypeScript-JavaScript-and-Dart
 
                 
                It’s not that JavaScript has no type system. There was just no way to formalize it
 
                It’s not that JavaScript has no type system. There was just no way to formalize it Anders Hejlsberg
 
                Red squiggly lines
 
                ⭐ 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
 
                ⭐ 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
 
                ⭐ 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.
 
                ⛩ Gradual, structural, generic ( Distinct value / type namespaces ) Extensive type inference * Control flow analysis & Object-oriented and functional
 
                Tooling
 
                TypeScript JavaScript
 
                TypeScript Code emitting TS Type System Modern JS BrowserJS
 
                This is a lot easier to handle Type System Modern JS BrowserJS
 
                 
                typescript files type checker compiler .ts, .tsx javascript files .js type definitions .d.ts
 
                typescript extra tool ⭐ Type checking $ Bundling ⭐ Type erasure $ Tree shaking ⭐ Transpilation $ Minifications ⭐ JSX Tranformations ⭐ Module resolution
 
                ambient type declarations .ts .ts .ts .ts .ts .ts Your codebase
 
                ambient type declarations .ts .ts Ambient declarations .ts “window” “Object” .ts .ts .ts Your codebase
 
                Type System Deep Dive Part 2 @ddprrt - fettblog.eu - typescript-book.com
 
                ⛩ Gradual, structural, generic ( Distinct value / type namespaces + ) Extensive type inference * Control flow analysis + & Object-oriented and functional
 
                Type namespaces
 
                primitive types number Symbol boolean Object undefined null string
 
                top types any unknown number Symbol boolean Object undefined null string
 
                bottom types never number Symbol boolean Object undefined null string
 
                primitive types number Symbol boolean Object undefined null string
 
                primitive types number boolean undefined null string
 
                number value types … -1 boolean NaN 1000000 6 120.3 true false string … undefined null ‘Hello world’ ‘Baumi’
 
                value types … -1 NaN 1000000 6 120.3 true false … undefined null ‘Hello world’ ‘Baumi’
 
                number value types … -1 boolean NaN 1000000 6 120.3 true false string … undefined null ‘Hello world’ ‘Baumi’
 
                number | string | undefined number … -1 boolean NaN 1000000 6 120.3 true false string … undefined null ‘Hello world’ ‘Baumi’
 
                Intersection types { a: string } { } { b: string } a: string, b: string
 
                Type Hierarchy in TypeScript any / unknown Number String Enum Enum Symbol Boolean Class Unique null undefined never Object Union Array Intersection Tuple Function Conditional
 
                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
 
                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
 
                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
 
                any unknown The Wildcard The Cautious type Type safety is in the developer’s responsibility Type safety is in the compiler’s responsibility
 
                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
 
                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
 
                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
 
                Arrays vs Tuples const person = [39, “Stefan”]; const [age, name] = person; const person: [number, string] = [39, “Stefan”]; const [age, name] = person;
 
                Arrays vs Tuples const person = [39, “Stefan”]; person: (string | number)[] const [age, name] = person; const person: [number, string] = [39, “Stefan”]; const [age, name] = person;
 
                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;
 
                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]
 
                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]
 
                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]
 
                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
 
                Control flow analysis
 
                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
 
                Control flow analysis Narrowing • Using typeof guards • Using instance of guards • Truthiness (excludes null and undefined) • Discriminated unions • Type predicates
 
                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 }
 
                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; }
 
                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; }
 
                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; }
 
                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; }
 
                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; }
 
                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
 
                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
 
                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
 
                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
 
                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
 
                Structural typing
 
                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
 
                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
 
                interface type Contract to the outside Contract in your codebase Can be modified from users Allows for advanced type system features
 
                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
 
                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
 
                Generics & Conditional Types Part 3 @ddprrt - fettblog.eu - typescript-book.com
 
                Generics
 
                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
 
                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
 
                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”
 
                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 }
 
                Conditional types
 
                Function overloads vs Conditional types
 
                Advanced Types Part 4 @ddprrt - fettblog.eu - typescript-book.com
 
                Promises and async/await
 
                String template literal types
 
                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
 
                Variadic Tuple Types
 
                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
 
                Methodologies & Gotchas Part 5 @ddprrt - fettblog.eu - typescript-book.com
 
                Low maintenance types
 
                Accumulator technique
 
                Void
 
                The problem with Enums
 
                Error Handling Gotchas
 
                Unexpected Intersections
 
                Thank you.
