├── .gitignore ├── 01-non-nullable-types ├── src │ └── index.ts └── tsconfig.json ├── 02-control-flow-based-type-analysis ├── src │ └── index.ts └── tsconfig.json ├── 03-type-guard-functions ├── src │ └── index.ts └── tsconfig.json ├── 04-readonly-properties-and-index-signatures ├── src │ └── index.ts └── tsconfig.json ├── 05-the-object-type ├── src │ └── index.ts └── tsconfig.json ├── 06-the-never-type ├── src │ └── index.ts └── tsconfig.json ├── 07-function-overloads ├── src │ └── index.ts └── tsconfig.json ├── 08-string-enums ├── src │ └── index.ts └── tsconfig.json ├── 09-literal-types ├── src │ └── index.ts └── tsconfig.json ├── 10-discriminated-union-types ├── src │ └── index.ts └── tsconfig.json ├── 11-rest-and-spread-types ├── src │ └── index.ts └── tsconfig.json ├── 12-keyof-and-lookup-types ├── src │ └── index.ts └── tsconfig.json ├── 13-mapped-types ├── src │ └── index.ts └── tsconfig.json ├── README.md └── course-artwork.png /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | dist 3 | -------------------------------------------------------------------------------- /01-non-nullable-types/src/index.ts: -------------------------------------------------------------------------------- 1 | function trimAndLower(text: string | null | undefined) { 2 | if (typeof text === "string") { 3 | return text.trim().toLowerCase(); 4 | } 5 | return text; 6 | } 7 | 8 | console.log(trimAndLower(" LearnTypeScript.io ")); 9 | console.log(trimAndLower(null)); 10 | console.log(trimAndLower(undefined)); 11 | 12 | // ======================================== 13 | 14 | const container = document.getElementById("app-container")!; 15 | container.remove(); 16 | -------------------------------------------------------------------------------- /01-non-nullable-types/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "target": "es5", 4 | "rootDir": "src", 5 | "outDir": "dist", 6 | "strictNullChecks": true 7 | } 8 | } 9 | -------------------------------------------------------------------------------- /02-control-flow-based-type-analysis/src/index.ts: -------------------------------------------------------------------------------- 1 | function trimAndLower(text: string | null | undefined) { 2 | text; 3 | 4 | if (typeof text === "string") { 5 | text; 6 | 7 | return text.trim().toLowerCase(); 8 | } 9 | 10 | text; 11 | 12 | return text; 13 | } 14 | -------------------------------------------------------------------------------- /02-control-flow-based-type-analysis/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "target": "es5", 4 | "rootDir": "src", 5 | "outDir": "dist", 6 | "strictNullChecks": true 7 | } 8 | } 9 | -------------------------------------------------------------------------------- /03-type-guard-functions/src/index.ts: -------------------------------------------------------------------------------- 1 | const numbers = [0, 1, 2, [3, 4], 5, [6], [7], 8, [9]]; 2 | 3 | function isFlat(array: (T | T[])[]): array is T[] { 4 | return !array.some(Array.isArray); 5 | } 6 | 7 | if (isFlat(numbers)) { 8 | numbers; 9 | } 10 | -------------------------------------------------------------------------------- /03-type-guard-functions/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "target": "es5", 4 | "rootDir": "src", 5 | "outDir": "dist", 6 | "strictNullChecks": true 7 | } 8 | } 9 | -------------------------------------------------------------------------------- /04-readonly-properties-and-index-signatures/src/index.ts: -------------------------------------------------------------------------------- 1 | const weekdays: ReadonlyArray = [ 2 | "Sunday", 3 | "Monday", 4 | "Tuesday", 5 | "Wednesday", 6 | "Thursday", 7 | "Friday", 8 | "Saturday" 9 | ]; 10 | -------------------------------------------------------------------------------- /04-readonly-properties-and-index-signatures/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "target": "es5", 4 | "rootDir": "src", 5 | "outDir": "dist", 6 | "strictNullChecks": true 7 | } 8 | } 9 | -------------------------------------------------------------------------------- /05-the-object-type/src/index.ts: -------------------------------------------------------------------------------- 1 | const obj: { [key: string]: string } = {}; 2 | obj.hasOwnProperty("foo"); 3 | obj.foo = "bar"; 4 | -------------------------------------------------------------------------------- /05-the-object-type/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "target": "es5", 4 | "rootDir": "src", 5 | "outDir": "dist", 6 | "strictNullChecks": true 7 | } 8 | } 9 | -------------------------------------------------------------------------------- /06-the-never-type/src/index.ts: -------------------------------------------------------------------------------- 1 | enum ShirtSize { 2 | S, 3 | M, 4 | L, 5 | XL 6 | } 7 | 8 | function assertNever(value: never): never { 9 | throw Error(`Unexpected value '${value}'`); 10 | } 11 | 12 | function prettyPrint(size: ShirtSize) { 13 | switch (size) { 14 | case ShirtSize.S: return "small"; 15 | case ShirtSize.M: return "medium"; 16 | case ShirtSize.L: return "large"; 17 | case ShirtSize.XL: return "extra large"; 18 | default: return assertNever(size); 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /06-the-never-type/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "target": "es5", 4 | "rootDir": "src", 5 | "outDir": "dist", 6 | "strictNullChecks": true 7 | } 8 | } 9 | -------------------------------------------------------------------------------- /07-function-overloads/src/index.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * Reverses the given string. 3 | * @param string The string to reverse. 4 | */ 5 | function reverse(string: string): string; 6 | 7 | /** 8 | * Reverses the given array. 9 | * @param array The array to reverse. 10 | */ 11 | function reverse(array: T[]): T[]; 12 | 13 | function reverse(stringOrArray: string | any[]) { 14 | return typeof stringOrArray === "string" 15 | ? [...stringOrArray].reverse().join("") 16 | : stringOrArray.slice().reverse(); 17 | } 18 | 19 | const reversedString = reverse("TypeScript"); 20 | const reversedArray = reverse([4, 8, 15, 16, 23, 42]); 21 | -------------------------------------------------------------------------------- /07-function-overloads/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "target": "es2015", 4 | "rootDir": "src", 5 | "outDir": "dist", 6 | "strictNullChecks": true 7 | } 8 | } 9 | -------------------------------------------------------------------------------- /08-string-enums/src/index.ts: -------------------------------------------------------------------------------- 1 | const enum MediaTypes { 2 | JSON = "application/json" 3 | } 4 | 5 | fetch("https://example.com/api/endpoint", { 6 | headers: { 7 | Accept: MediaTypes.JSON 8 | } 9 | }).then(response => { 10 | // ... 11 | }); 12 | -------------------------------------------------------------------------------- /08-string-enums/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "target": "es5", 4 | "rootDir": "src", 5 | "outDir": "dist", 6 | "strictNullChecks": true, 7 | "preserveConstEnums": true 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /09-literal-types/src/index.ts: -------------------------------------------------------------------------------- 1 | let autoComplete: "on" | "off" = "on"; 2 | autoComplete = "off"; 3 | autoComplete = "ON"; 4 | 5 | // ============================================================ 6 | 7 | type NumberBase = 2 | 8 | 10 | 16; 8 | let base: NumberBase; 9 | base = 2; 10 | base = 3; 11 | 12 | // ============================================================ 13 | 14 | let autoFocus: true = true; 15 | autoFocus = false; 16 | 17 | // ============================================================ 18 | 19 | enum Protocols { 20 | HTTP, 21 | HTTPS, 22 | FTP 23 | } 24 | 25 | type HyperTextProtocol = Protocols.HTTP | Protocols.HTTPS; 26 | 27 | let protocol: HyperTextProtocol; 28 | protocol = Protocols.HTTP; 29 | protocol = Protocols.HTTPS; 30 | protocol = Protocols.FTP; 31 | -------------------------------------------------------------------------------- /09-literal-types/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "target": "es5", 4 | "rootDir": "src", 5 | "outDir": "dist", 6 | "strictNullChecks": true 7 | } 8 | } 9 | -------------------------------------------------------------------------------- /10-discriminated-union-types/src/index.ts: -------------------------------------------------------------------------------- 1 | type Result = 2 | | { success: true; value: T } 3 | | { success: false; error: string }; 4 | 5 | function tryParseInt(text: string): Result { 6 | if (/^-?\d+$/.test(text)) { 7 | return { 8 | success: true, 9 | value: parseInt(text, 10) 10 | }; 11 | } 12 | return { 13 | success: false, 14 | error: "Invalid number format" 15 | }; 16 | } 17 | 18 | const result = tryParseInt("42"); 19 | 20 | if (result.success) { 21 | result; 22 | } else { 23 | result; 24 | } 25 | 26 | // ============================================================ 27 | 28 | interface Cash { 29 | kind: "cash"; 30 | } 31 | 32 | interface PayPal { 33 | kind: "paypal"; 34 | email: string; 35 | } 36 | 37 | interface CreditCard { 38 | kind: "creditcard"; 39 | cardNumber: string; 40 | securityCode: string; 41 | } 42 | 43 | type PaymentMethod = Cash | PayPal | CreditCard; 44 | 45 | function stringifyPaymentMethod(method: PaymentMethod): string { 46 | switch (method.kind) { 47 | case "cash": 48 | return "Cash"; 49 | case "paypal": 50 | return `PayPal (${method.email})`; 51 | case "creditcard": 52 | return "Credit Card"; 53 | } 54 | } 55 | -------------------------------------------------------------------------------- /10-discriminated-union-types/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "target": "es5", 4 | "rootDir": "src", 5 | "outDir": "dist", 6 | "strictNullChecks": true 7 | } 8 | } 9 | -------------------------------------------------------------------------------- /11-rest-and-spread-types/src/index.ts: -------------------------------------------------------------------------------- 1 | const person = { 2 | fullName: "Marius Schulz", 3 | blog: "https://blog.mariusschulz.com", 4 | twitter: "@mariusschulz" 5 | }; 6 | 7 | const { fullName, ...socialMedia } = person; 8 | 9 | fullName; 10 | socialMedia; 11 | 12 | // ============================================================ 13 | 14 | const defaultStyles = { 15 | fontFamily: "Arial, sans-serif", 16 | fontWeight: "normal" 17 | }; 18 | 19 | const userStyles = { 20 | color: "#111111", 21 | fontWeight: 700 22 | }; 23 | 24 | const styles = { 25 | ...defaultStyles, 26 | ...userStyles 27 | }; 28 | 29 | // ============================================================ 30 | 31 | const todo = { 32 | text: "Water the flowers", 33 | completed: false, 34 | tags: ["garden"] 35 | }; 36 | 37 | const shallowCopy = { ...todo }; 38 | shallowCopy.text = "Mow the lawn"; 39 | shallowCopy.tags.push("weekend"); 40 | 41 | console.log(shallowCopy); 42 | console.log(todo); 43 | -------------------------------------------------------------------------------- /11-rest-and-spread-types/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "target": "es5", 4 | "rootDir": "src", 5 | "outDir": "dist", 6 | "strictNullChecks": true 7 | } 8 | } 9 | -------------------------------------------------------------------------------- /12-keyof-and-lookup-types/src/index.ts: -------------------------------------------------------------------------------- 1 | interface Todo { 2 | id: number; 3 | text: string; 4 | completed: boolean; 5 | } 6 | 7 | const todo: Todo = { 8 | id: 1, 9 | text: "Buy milk", 10 | completed: false 11 | }; 12 | 13 | function prop(obj: T, key: K): T[K] { 14 | return obj[key]; 15 | } 16 | 17 | const id = prop(todo, "id"); 18 | const text = prop(todo, "text"); 19 | const completed = prop(todo, "completed"); 20 | -------------------------------------------------------------------------------- /12-keyof-and-lookup-types/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "target": "es5", 4 | "rootDir": "src", 5 | "outDir": "dist", 6 | "strictNullChecks": true 7 | } 8 | } 9 | -------------------------------------------------------------------------------- /13-mapped-types/src/index.ts: -------------------------------------------------------------------------------- 1 | interface Point { 2 | x: number; 3 | y: number; 4 | } 5 | 6 | type Nullable = { 7 | [P in keyof T]: T[P] | null; 8 | }; 9 | 10 | type Stringify = { 11 | [P in keyof T]: string; 12 | }; 13 | 14 | type PartialNullablePoint = Partial>>; 15 | 16 | let point: PartialNullablePoint; 17 | point = { x: "0", y: "0" }; 18 | point = { x: "0" }; 19 | point = { x: undefined, y: null }; 20 | -------------------------------------------------------------------------------- /13-mapped-types/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "target": "es5", 4 | "rootDir": "src", 5 | "outDir": "dist", 6 | "strictNullChecks": true 7 | } 8 | } 9 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Advanced Static Types in TypeScript 2 | 3 | This repository contains the code for my [Advanced Static Types in TypeScript](https://egghead.io/courses/advanced-static-types-in-typescript) video course. 4 | 5 | Advanced Static Types in TypeScript 6 | 7 | ## Lessons 8 | 9 | 1. [Restrict null and undefined via Non-Nullable-Types in TypeScript](https://egghead.io/lessons/typescript-restrict-null-and-undefined-via-non-nullable-types-in-typescript) 10 | 2. [Understand TypeScript’s Control Flow Based Type Analysis](https://egghead.io/lessons/typescript-understand-typescript-s-control-flow-based-type-analysis) 11 | 3. [Define Custom Type Guard Functions in TypeScript](https://egghead.io/lessons/typescript-define-custom-type-guard-functions-in-typescript) 12 | 4. [Make Properties and Index Signatures Readonly in TypeScript](https://egghead.io/lessons/typescript-make-properties-and-index-signatures-readonly-in-typescript) 13 | 5. [Represent Non-Primitive Types with TypeScript’s object Type](https://egghead.io/lessons/typescript-represent-non-primitive-types-with-typescript-s-object-type) 14 | 6. [Use TypeScript’s never Type for Exhaustiveness Checking](https://egghead.io/lessons/typescript-use-typescript-s-never-type-for-exhaustiveness-checking) 15 | 7. [Overload a Function with TypeScript’s Overload Signatures](https://egghead.io/lessons/typescript-overload-a-function-with-typescript-s-overload-signatures) 16 | 8. [Collect Related Strings in a String Enum in TypeScript](https://egghead.io/lessons/typescript-collect-related-strings-in-a-string-enum-in-typescript) 17 | 9. [Specify Exact Values with TypeScript’s Literal Types](https://egghead.io/lessons/typescript-specify-exact-values-with-typescript-s-literal-types) 18 | 10. [Model Alternatives with Discriminated Union Types in TypeScript](https://egghead.io/lessons/typescript-model-alternatives-with-discriminated-union-types-in-typescript) 19 | 11. [Infer Types for Rest and Spread Properties in TypeScript](https://egghead.io/lessons/typescript-infer-types-for-rest-and-spread-properties-in-typescript) 20 | 12. [Query Properties with keyof and Lookup Types in TypeScript](https://egghead.io/lessons/typescript-query-properties-with-keyof-and-lookup-types-in-typescript) 21 | 13. [Transform Existing Types Using Mapped Types in TypeScript](https://egghead.io/lessons/typescript-transform-existing-types-using-mapped-types-in-typescript) 22 | -------------------------------------------------------------------------------- /course-artwork.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mariusschulz/egghead-advanced-static-types-in-typescript/1749d5d37837cfb3512f38b8010d3807ae20228f/course-artwork.png --------------------------------------------------------------------------------