├── .gitignore ├── .vscode ├── launch.json ├── settings.json └── tasks.json ├── LICENSE ├── README.md ├── RUN_INSTRUCTIONS.md ├── chapters ├── chapter-1_Getting_Started_With_Typescript_4 │ ├── classDiagrams.ts │ ├── computeFrequency.ts │ ├── degToRad.ts │ ├── duckTyping.js │ ├── features.ts │ ├── inputOutput.ts │ ├── intro.ts │ ├── jsUtils.js │ ├── libraries.ts │ ├── migrating.ts │ ├── mul.test.ts │ ├── mul.ts │ ├── refactoring.ts │ ├── removeDuplicateVars.ts │ ├── structural.ts │ └── tsconfig.json ├── chapter-2_Core_Principles_and_use_cases │ ├── .gitkeep │ ├── DOM │ │ ├── index.d.ts │ │ ├── index.html │ │ ├── index.js │ │ ├── index.js.map │ │ ├── index.ts │ │ ├── tsconfig.json │ │ └── tsconfig.tsbuildinfo │ ├── advanced.ts │ ├── deno │ │ └── index.ts │ ├── express │ │ ├── app.ts │ │ ├── container.ts │ │ ├── health.controller.ts │ │ └── server.ts │ ├── node-error │ │ └── index.ts │ ├── oop.ts │ ├── partial.ts │ ├── react │ │ ├── app.tsx │ │ ├── index.html │ │ ├── tsconfig.json │ │ └── webpack.config.js │ ├── tsconfig.json │ ├── utility.ts │ └── webpack │ │ ├── index.html │ │ ├── index.js │ │ ├── index.ts │ │ ├── tsconfig.json │ │ └── webpack.config.js ├── chapter-3_Creational_Design_Patterns │ ├── .gitkeep │ ├── AbstractFactory.test.ts │ ├── AbstractFactory.ts │ ├── FactoryMethod.test.ts │ ├── FactoryMethod.ts │ ├── FactoryMethodType.ts │ ├── InversifySingleton.ts │ ├── ModuleSingleton.ts │ ├── ParametricSingleton.ts │ ├── Prototype.test.ts │ ├── Prototype.ts │ ├── ProxyBuilder.ts │ ├── UsersApiSingleton.ts │ ├── WebsiteBuilder.test.ts │ ├── WebsiteBuilder.ts │ ├── singleton.test.ts │ └── singleton.ts ├── chapter-4_Structural_Design_Patterns │ ├── .gitkeep │ ├── Adapter.test.ts │ ├── Adapter.ts │ ├── Bridge.ts │ ├── Composite.ts │ ├── Decorator.test.ts │ ├── Decorator.ts │ ├── EventCreator.ts │ ├── Facade.ts │ ├── FacadeExample.ts │ ├── Flyweight.ts │ ├── Proxy.es6.ts │ └── Proxy.ts ├── chapter-5_Behavioral_Design_Patterns │ ├── .gitkeep │ ├── ChainOfResponsibility.test.ts │ ├── ChainOfResponsibility.ts │ ├── Command.ts │ ├── Iterator.ts │ ├── Mediator.ts │ ├── Memento.ts │ ├── Observer.ts │ ├── State.test.ts │ ├── State.ts │ ├── StateButton.ts │ ├── Strategy.ts │ ├── TemplateMethod.ts │ └── Visitor.ts ├── chapter-6_Functional_Programming_Concepts │ ├── .gitkeep │ ├── FirstClassCitizens.ts │ ├── FunctionComposition.ts │ ├── Immutability.ts │ ├── Lens.ts │ ├── Monad.ts │ ├── Monocle.ts │ ├── PureFunctions.ts │ ├── Recursion.ts │ ├── Referential.ts │ ├── Transducer.ts │ └── tempCodeRunnerFile.ts ├── chapter-7_Reactive_Programming_concepts │ ├── .gitkeep │ ├── Futures.ts │ ├── Observables.ts │ ├── Operators.ts │ ├── Patterns.ts │ ├── Promises.ts │ └── example.ts ├── chapter-8_Best_practices │ ├── .gitkeep │ ├── DDD.ts │ ├── PatternCombinations.ts │ ├── SOLID.ts │ └── Utilities.ts ├── chapter-9_Anti_patterns │ ├── .gitkeep │ ├── ClassesFatigue.ts │ ├── LanguageIdioms.ts │ ├── PermissiveTypes.ts │ ├── RuntimeAssertions.ts │ ├── TypeInference.ts │ └── inference.ts ├── smoke.ts └── tsconfig.json ├── example.txt ├── images └── playground_code.png ├── jest.config.js ├── package-lock.json ├── package.json └── tsconfig.json /.gitignore: -------------------------------------------------------------------------------- 1 | pids 2 | logs 3 | results 4 | tmp 5 | 6 | # Coverage reports 7 | coverage 8 | 9 | # API keys and secrets 10 | .env 11 | 12 | # Dependency directory 13 | node_modules 14 | bower_components 15 | 16 | # Editors 17 | .idea 18 | *.iml 19 | 20 | # OS metadata 21 | .DS_Store 22 | Thumbs.db 23 | 24 | # Ignore built ts files 25 | dist/**/* 26 | 27 | # ignore yarn.lock 28 | yarn.lock -------------------------------------------------------------------------------- /.vscode/launch.json: -------------------------------------------------------------------------------- 1 | { 2 | // Use IntelliSense to learn about possible Node.js debug attributes. 3 | // Hover to view descriptions of existing attributes. 4 | // For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387 5 | "version": "0.2.0", 6 | "configurations": [ 7 | { 8 | "name": "Run Smoke Test Program", 9 | "type": "node", 10 | "program": "${workspaceFolder}/dist/smoke.js", 11 | "request": "launch", 12 | "preLaunchTask": "npm: build" 13 | }, 14 | { 15 | "type": "node", 16 | "request": "attach", 17 | "name": "Attach by Process ID", 18 | "processId": "${command:PickProcess}", 19 | "protocol": "inspector" 20 | }, 21 | { 22 | "type": "node", 23 | "request": "launch", 24 | "name": "Run Code from Chapter 1", 25 | "program": "${workspaceFolder}/chapters/chapter-1_Getting_Started_With_Typescript_4/${input:programNameChapter1}", 26 | "preLaunchTask": "tsc: build Chapter 1", 27 | "outFiles": ["${workspaceFolder}/dist/**/*.js"], 28 | }, 29 | { 30 | "type": "node", 31 | "request": "launch", 32 | "name": "Run Code from Chapter 2", 33 | "program": "${workspaceFolder}/chapters/chapter-2_Core_Principles_and_use_cases/${input:programNameChapter2}", 34 | "preLaunchTask": "tsc: build Chapter 2", 35 | "outFiles": ["${workspaceFolder}/dist/**/*.js"], 36 | }, 37 | ], 38 | "inputs": [ 39 | { 40 | "type": "pickString", 41 | "id": "programNameChapter1", 42 | "description": "What program you want to launch?", 43 | "options": [ 44 | "intro.ts", 45 | "inputOutput.ts", 46 | "classDiagrams.ts", 47 | "computeFrequency.ts", 48 | "removeDuplicateVars.ts", 49 | "refactoring.ts", 50 | "libraries.ts", 51 | "features.ts" 52 | ], 53 | "default": "computeFrequency.ts" 54 | }, 55 | { 56 | "type": "pickString", 57 | "id": "programNameChapter2", 58 | "description": "What program you want to launch?", 59 | "options": ["intro.ts"], 60 | "default": "intro.ts" 61 | }, 62 | ] 63 | } -------------------------------------------------------------------------------- /.vscode/settings.json: -------------------------------------------------------------------------------- 1 | { 2 | "js/ts.implicitProjectConfig.strictFunctionTypes": false 3 | } -------------------------------------------------------------------------------- /.vscode/tasks.json: -------------------------------------------------------------------------------- 1 | { 2 | // See https://go.microsoft.com/fwlink/?LinkId=733558 3 | // for the documentation about the tasks.json format 4 | "version": "2.0.0", 5 | "tasks": [ 6 | { 7 | "type": "npm", 8 | "script": "build", 9 | "group": { 10 | "kind": "build", 11 | "isDefault": true 12 | } 13 | }, 14 | { 15 | "type": "typescript", 16 | "tsconfig": "chapters/chapter-1_Getting_Started_With_Typescript_4/tsconfig.json", 17 | "problemMatcher": [ 18 | "$tsc" 19 | ], 20 | "group": "build", 21 | "label": "tsc: build Chapter 1" 22 | }, 23 | { 24 | "type": "typescript", 25 | "tsconfig": "chapters/chapter-2_Core_Principles_and_use_cases/DOM/tsconfig.json", 26 | "problemMatcher": [ 27 | "$tsc" 28 | ], 29 | "group": "build", 30 | "label": "tsc: build Chapter 2 HTML DOM Example" 31 | }, 32 | { 33 | "type": "typescript", 34 | "tsconfig": "chapters/chapter-2_Core_Principles_and_use_cases/tsconfig.json", 35 | "problemMatcher": [ 36 | "$tsc" 37 | ], 38 | "group": "build", 39 | "label": "tsc: build Chapter 2" 40 | }, 41 | { 42 | "type": "shell", 43 | "command": "deno", 44 | "args": [ "run", "--allow-net=0.0.0.0:8000", "chapters/chapter-2_Core_Principles_and_use_cases/deno/index.ts"], 45 | "label": "tsc: run Chapter 2 Deno Example" 46 | }, 47 | ], 48 | } 49 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2021 Packt 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | # TypeScript 4 Design Patterns and Best Practices 5 | 6 | 7 | 8 | This is the code repository for [TypeScript 4 Design Patterns and Best Practices](https://www.packtpub.com/product/typescript-4-design-patterns-and-best-practices/9781800563421), published by Packt. 9 | 10 | **Discover effective techniques and design patterns for every programming task** 11 | 12 | ## What is this book about? 13 | TypeScript offers a rich type system that can be used to enhance the existing JavaScript code. This book will cover all the classical design patterns in TypeScript and their primary categories using practical examples and class diagrams for each of the design patterns. 14 | 15 | This book covers the following exciting features: 16 | * Understand the role of design patterns and their significance 17 | * Explore all significant design patterns within the context of TypeScript 18 | * Find out how design patterns differ from design concepts 19 | * Understand how to put the principles of design patterns into practice 20 | * Discover additional patterns that stem from functional and reactive programming 21 | * Recognize common gotchas and antipatterns when developing TypeScript applications and understand how to avoid them 22 | 23 | If you feel this book is for you, get your [copy](https://www.amazon.com/dp/1800563426) today! 24 | 25 | https://www.packtpub.com/ 27 | 28 | ## Instructions and Navigations 29 | All of the code is organized into folders. For example, Chapter02. 30 | 31 | The code will look like the following: 32 | ``` 33 | export class EventCreator implements EventSender { 34 | sendEvent(action: string): void { 35 | console.log("Event Created: ", action); 36 | } 37 | } 38 | ``` 39 | 40 | **Following is what you need for this book:** 41 | If you're a developer looking to learn how to apply established design patterns to solve common programming problems instead of reinventing solutions, you'll find this book useful. You're not expected to have prior knowledge of design patterns. Basic TypeScript knowledge is all you need to get started with this book. 42 | 43 | With the following software and hardware list you can run all code files present in the book (Chapter 1-9). 44 | ### Software and Hardware List 45 | | Chapter | Software required | OS required | 46 | | -------- | ------------------------------------ | ----------------------------------- | 47 | | 1 - 9 | Visual Studio Code 1.49, Node.js 12.18.3, TypeScript 4.1 | Windows, Mac OS X, and Linux (Any) | 48 | 49 | We also provide a PDF file that has color images of the screenshots/diagrams used in this book. [Click here to download it](https://static.packt-cdn.com/downloads/9781800563421_ColorImages.pdf). 50 | 51 | ### Related products 52 | * Mastering TypeScript 3 - Third Edition [[Packt]](https://www.packtpub.com/product/mastering-typescript-3-third-edition/9781789536706?utm_source=github&utm_medium=repository&utm_campaign=9781789536706) [[Amazon]](https://www.amazon.com/dp/1789536707) 53 | 54 | * Advanced TypeScript 3 Programming Projects [[Packt]](https://www.packtpub.com/product/advanced-typescript-3-programming-projects/9781789133042?utm_source=github&utm_medium=repository&utm_campaign=9781789133042) [[Amazon]](https://www.amazon.com/dp/1789133041) 55 | 56 | ## Get to Know the Author 57 | **Theo Despoudis** 58 | lives in Ireland, where he works as a Software Engineer for WP Engine and as a part-time tech practitioner for Fixate. He is the co-author of The React Workshop and Advanced Go Programming in 7 Days, Dzone Core Member, and maintains some open source projects on GitHub. Theo is available for conference talks, independent consulting, and corporate training services opportunities. 59 | ### Download a free PDF 60 | 61 | If you have already purchased a print or Kindle version of this book, you can get a DRM-free PDF version at no cost.
Simply click on the link to claim your free PDF.
62 |

https://packt.link/free-ebook/9781800563421

-------------------------------------------------------------------------------- /RUN_INSTRUCTIONS.md: -------------------------------------------------------------------------------- 1 | # Typescript 4 Design Patterns and Best Practices 2 | 3 | This is the code repository for Typescript 4 Design Patterns and Best Practices, published by Packt. 4 | 5 | ## How to run the code examples? 6 | 7 | To be able to run the examples, you first need to install all the required dependencies using the following command: 8 | 9 | ```sh 10 | npm install 11 | ``` 12 | 13 | We have configured a local version of `ts-node` and `Typescript` so you don't have to install globally. 14 | 15 | If you have compatible TypeScript compiler installed globally, you can just use the global `ts-node` command to run target code bundle. 16 | 17 | You can then run the `ts` npm task providing the relative path of the script: 18 | 19 | ``` 20 | npm run ts -- chapters/smoke.ts 21 | > ts-node "chapters/smoke.ts" 22 | 1 23 | 2 24 | 3 25 | 4 26 | Hello 27 | 1 28 | ``` 29 | 30 | Alternatiely you may want to build and run the compiled javascript from the `dist` folder: 31 | 32 | ``` 33 | npm run build 34 | > npx tsc --build chapters 35 | 36 | node dist/smoke.js 37 | 1 38 | 2 39 | 3 40 | 4 41 | Hello 42 | 1 43 | ``` 44 | 45 | In some cases you may want to quickly see the compiled javascript or to quickly test some examples using the [TypeScript Playground](https://www.typescriptlang.org/play/index.html). You can copy the code in the playground (left) to get the resulting JavaScript output (right) 46 | 47 | ![](./images/playground_code.png) 48 | 49 | ## Code examples 50 | 51 | - [Chapter 1: Getting Started With Typescript 4](./chapters/chapter-1_Getting_Started_With_Typescript_4/) 52 | -------------------------------------------------------------------------------- /chapters/chapter-1_Getting_Started_With_Typescript_4/classDiagrams.ts: -------------------------------------------------------------------------------- 1 | interface Identifiable { 2 | id: T; 3 | } 4 | 5 | class Customer {} 6 | 7 | class Product implements Identifiable { 8 | id: string; 9 | constructor(id: string) { 10 | this.id = id; 11 | } 12 | } 13 | 14 | abstract class BaseAPIClient {} 15 | 16 | // Association 17 | class Blog implements Identifiable { 18 | id: string; 19 | authorId: string; 20 | constructor(id: string, authorId: string) { 21 | this.id = id; 22 | this.authorId = authorId; 23 | } 24 | } 25 | 26 | class Author {} 27 | 28 | // Aggregation 29 | class QueryBuilder {} 30 | class EmptyQueryBuilder extends QueryBuilder {} 31 | 32 | interface SearchParams { 33 | qb?: QueryBuilder; 34 | path: string; 35 | } 36 | 37 | class SearchService { 38 | queryBuilder?: QueryBuilder; 39 | path: string; 40 | 41 | constructor({ qb = EmptyQueryBuilder, path }: SearchParams) { 42 | this.queryBuilder = qb; 43 | this.path = path; 44 | } 45 | } 46 | 47 | // Composition 48 | class Directory { 49 | files: File[]; 50 | directories: Directory[]; 51 | constructor(files: File[], directories: Directory[]) { 52 | this.files = files; 53 | this.directories = directories; 54 | } 55 | 56 | addFile(file: File): void { 57 | this.files.push(file); 58 | } 59 | addDir(directory: Directory): void { 60 | this.directories.push(directory); 61 | } 62 | } 63 | 64 | // Visibility 65 | class SSHUser { 66 | private privateKey: string; 67 | public publicKey: string; 68 | 69 | constructor(prvKey: string, pubKey: string) { 70 | this.privateKey = prvKey; 71 | this.publicKey = pubKey; 72 | } 73 | 74 | public getBase64(): string { 75 | return Buffer.from(this.publicKey).toString("base64"); 76 | } 77 | } 78 | 79 | // Inheritance 80 | 81 | class BaseClient {} 82 | class UsersApiClient extends BaseClient {} 83 | -------------------------------------------------------------------------------- /chapters/chapter-1_Getting_Started_With_Typescript_4/computeFrequency.ts: -------------------------------------------------------------------------------- 1 | function computeFrequency(input: string) { 2 | const freqTable = new Map(); 3 | for (let ch of input) { 4 | if (!freqTable.has(ch)) { 5 | freqTable.set(ch, 1); 6 | } else { 7 | freqTable.set(ch, freqTable.get(ch) + 1); 8 | } 9 | } 10 | 11 | return freqTable; 12 | } 13 | 14 | console.log(computeFrequency("12345")); 15 | -------------------------------------------------------------------------------- /chapters/chapter-1_Getting_Started_With_Typescript_4/degToRad.ts: -------------------------------------------------------------------------------- 1 | const degToRad = (degree): number => (degree * Math.PI) / 180; 2 | -------------------------------------------------------------------------------- /chapters/chapter-1_Getting_Started_With_Typescript_4/duckTyping.js: -------------------------------------------------------------------------------- 1 | export function triggerNotification(emailClient, logger) { 2 | if (logger && typeof logger.log === "function") { 3 | logger.log("Sending email"); 4 | } 5 | if (emailClient && typeof emailClient.send === "function") { 6 | emailClient.send("Message Sent"); 7 | } 8 | } 9 | 10 | triggerNotification( 11 | { log: () => console.log("Logger call") }, 12 | { send: (msg) => console.log(msg) } 13 | ); // Nothing! 14 | triggerNotification( 15 | { send: (msg) => console.log(msg) }, 16 | { log: () => console.log("Logger call") } 17 | ); // Works! 18 | -------------------------------------------------------------------------------- /chapters/chapter-1_Getting_Started_With_Typescript_4/features.ts: -------------------------------------------------------------------------------- 1 | type Point2d = [number, number]; 2 | type Point3d = [number, number, number]; 3 | 4 | const point1: Point2d = [1, 2]; 5 | const point2: Point3d = [1, 2, 3]; 6 | 7 | type NamedType = [string, ...T]; 8 | type NamedPoint2d = NamedType; 9 | 10 | const point3: NamedPoint2d = ["Point: (1, 2)", 1, 2]; 11 | 12 | type Point2dL = [x: number, y: number]; 13 | type Point3dL = [x: number, y: number, z: number]; 14 | 15 | type Suit = `${"Spade" | "Heart" | "Diamond" | "Club"}`; 16 | type Rank = `${ 17 | | "2" 18 | | "3" 19 | | "4" 20 | | "5" 21 | | "6" 22 | | "7" 23 | | "8" 24 | | "9" 25 | | "10" 26 | | "Jack" 27 | | "Queen" 28 | | "King" 29 | | "Ace"}`; 30 | 31 | type Deck = `${Rank} of ${Suit}`; 32 | -------------------------------------------------------------------------------- /chapters/chapter-1_Getting_Started_With_Typescript_4/inputOutput.ts: -------------------------------------------------------------------------------- 1 | const stream = process.stdin; 2 | setImmediate(function () { 3 | stream.push(null); 4 | }); 5 | 6 | stream.pipe(process.stdout); 7 | -------------------------------------------------------------------------------- /chapters/chapter-1_Getting_Started_With_Typescript_4/intro.ts: -------------------------------------------------------------------------------- 1 | const one: string = "one"; 2 | const two: boolean = false; 3 | const three: number = 3; 4 | const four: null = null; 5 | const five: unknown = 5; 6 | const six: any = 6; 7 | const seven: unique symbol = Symbol("seven"); 8 | let eight: never; // note that const eight: never cannot happen as we cannot instantiate a never 9 | let nine: bigint = BigInt(8); 10 | 11 | enum Keys { 12 | Up, 13 | Down, 14 | Left, 15 | Right, 16 | } 17 | let up: Keys = Keys.Up; 18 | 19 | const enum Bool { 20 | True, 21 | False, 22 | } 23 | 24 | let truth: Bool = Bool.True; 25 | 26 | const arr: number[] = [1, 2, 3]; // array of numbers of any size 27 | const tup: [number] = [1]; 28 | 29 | class User { 30 | private name: string; 31 | constructor(name: string) { 32 | this.name = name; 33 | } 34 | 35 | public getName(): string { 36 | return this.name; 37 | } 38 | } 39 | 40 | const user = new User("Theo"); 41 | console.log(user.getName()); 42 | 43 | abstract class BaseApiClient { 44 | abstract fetch(req: any): Promise; // must be implemented in sub-classes 45 | } 46 | 47 | class UsersClient extends BaseApiClient { 48 | fetch(req: any): Promise { 49 | return Promise.resolve([]); 50 | } 51 | } 52 | 53 | const client = new UsersClient(); 54 | client.fetch({ url: "/users" }); 55 | 56 | interface Comparable { 57 | compareTo(o: T): number; 58 | } 59 | 60 | interface AppConfig { 61 | paths: { 62 | base: string; 63 | }; 64 | maxRetryCount?: number; 65 | } 66 | 67 | const appConfig: AppConfig = { 68 | paths: { 69 | base: "/", 70 | }, 71 | }; 72 | 73 | type A = "A"; 74 | type B = "B"; 75 | 76 | type C = A & B; // type is never 77 | type D = C | "E"; // type is "E" 78 | type E = { 79 | name: string; 80 | }; 81 | 82 | type F = E & { 83 | age: number; 84 | }; 85 | 86 | let e: F = { 87 | name: "Theo", 88 | age: 20, 89 | }; 90 | -------------------------------------------------------------------------------- /chapters/chapter-1_Getting_Started_With_Typescript_4/jsUtils.js: -------------------------------------------------------------------------------- 1 | const isObject = (o) => { 2 | return o === Object(o) && !isArray(o) && typeof o !== "function"; 3 | }; 4 | 5 | const isArray = (arr) => { 6 | return Array.isArray(a); 7 | }; 8 | -------------------------------------------------------------------------------- /chapters/chapter-1_Getting_Started_With_Typescript_4/libraries.ts: -------------------------------------------------------------------------------- 1 | import "reflect-metadata"; 2 | import { Container, injectable, inject } from "inversify"; 3 | 4 | @injectable() 5 | class LoggerService { 6 | public logRequest() { 7 | console.log("call API Request"); 8 | } 9 | } 10 | 11 | @injectable() 12 | class ApiService { 13 | private logger: LoggerService; 14 | 15 | public constructor(logger: LoggerService) { 16 | this.logger = logger; 17 | } 18 | 19 | public request() { 20 | this.logger.logRequest(); 21 | return Promise.resolve([]); 22 | } 23 | } 24 | 25 | var container = new Container(); 26 | container.bind(LoggerService).to(LoggerService); 27 | container.bind(ApiService).to(ApiService); 28 | 29 | const apiService = container.resolve(ApiService); 30 | apiService.request(); 31 | -------------------------------------------------------------------------------- /chapters/chapter-1_Getting_Started_With_Typescript_4/migrating.ts: -------------------------------------------------------------------------------- 1 | import { triggerNotification } from "./duckTyping"; 2 | const isObject = require("./jsUtils"); 3 | 4 | triggerNotification( 5 | { send: (msg) => console.log(msg) }, 6 | { log: () => console.log("Logger call") } 7 | ); 8 | 9 | isObject([]); 10 | -------------------------------------------------------------------------------- /chapters/chapter-1_Getting_Started_With_Typescript_4/mul.test.ts: -------------------------------------------------------------------------------- 1 | import mul from "./mul"; 2 | 3 | test("multiplies 2 and 3 to give 12", () => { 4 | expect(mul(2, 3)).toBe(6); 5 | }); 6 | -------------------------------------------------------------------------------- /chapters/chapter-1_Getting_Started_With_Typescript_4/mul.ts: -------------------------------------------------------------------------------- 1 | function mul(a: number, b: number) { 2 | return a * b; 3 | } 4 | 5 | export default mul; 6 | -------------------------------------------------------------------------------- /chapters/chapter-1_Getting_Started_With_Typescript_4/refactoring.ts: -------------------------------------------------------------------------------- 1 | function find(arr: T[], predicate: (item: T) => boolean) { 2 | for (let item of arr) { 3 | if (predicate(item)) { 4 | return item; 5 | } 6 | } 7 | return undefined; 8 | } 9 | 10 | function indexOf(arr: T[], predicate: (item: T) => boolean) { 11 | for (let i = 0; i < arr.length; i += 1) { 12 | if (predicate(arr[i])) { 13 | return i; 14 | } 15 | } 16 | return -1; 17 | } 18 | -------------------------------------------------------------------------------- /chapters/chapter-1_Getting_Started_With_Typescript_4/removeDuplicateVars.ts: -------------------------------------------------------------------------------- 1 | function removeDuplicateChars(input: string) { 2 | const result: string[] = []; 3 | //const result = ["a"]; 4 | let seen = new Set(); 5 | for (let c of input) { 6 | if (!seen.has(c)) { 7 | seen.add(c); 8 | result.push(c); 9 | } 10 | } 11 | } 12 | 13 | console.log(removeDuplicateChars("aarfqwevzxcddd")); 14 | -------------------------------------------------------------------------------- /chapters/chapter-1_Getting_Started_With_Typescript_4/structural.ts: -------------------------------------------------------------------------------- 1 | interface Logger { 2 | log: (msg: string) => void; 3 | } 4 | 5 | let logger: Logger; 6 | let cat = { log: (msg: string) => console.log(msg) }; 7 | logger = cat; 8 | -------------------------------------------------------------------------------- /chapters/chapter-1_Getting_Started_With_Typescript_4/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "../../tsconfig.json", 3 | "compilerOptions": { 4 | // compiler options 5 | "composite": true, 6 | "allowJs": true, 7 | "noImplicitAny": true 8 | } 9 | } -------------------------------------------------------------------------------- /chapters/chapter-2_Core_Principles_and_use_cases/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PacktPublishing/TypeScript-4-Design-Patterns-and-Best-Practices/794888bd4c351b9565e01828440e3d0cf06d26c6/chapters/chapter-2_Core_Principles_and_use_cases/.gitkeep -------------------------------------------------------------------------------- /chapters/chapter-2_Core_Principles_and_use_cases/DOM/index.d.ts: -------------------------------------------------------------------------------- 1 | declare const p: Element; 2 | declare const span: HTMLSpanElement; 3 | declare const button: HTMLButtonElement | null; 4 | -------------------------------------------------------------------------------- /chapters/chapter-2_Core_Principles_and_use_cases/DOM/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Document 5 | 6 | 7 | 8 |
Typescript 4 Design Patterns
9 |

10 | 11 | 12 | 13 | 14 | -------------------------------------------------------------------------------- /chapters/chapter-2_Core_Principles_and_use_cases/DOM/index.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | var p = document.getElementsByClassName("paragraph")[0]; 3 | var span = document.createElement("span"); 4 | span.textContent = "This is a text we added dynamically"; 5 | p === null || p === void 0 ? void 0 : p.appendChild(span); 6 | var button = document.querySelector("button"); 7 | button === null || button === void 0 ? void 0 : button.addEventListener("click", function () { 8 | window.alert("You Clicked the Submit Button"); 9 | }); 10 | //# sourceMappingURL=index.js.map -------------------------------------------------------------------------------- /chapters/chapter-2_Core_Principles_and_use_cases/DOM/index.js.map: -------------------------------------------------------------------------------- 1 | {"version":3,"file":"index.js","sourceRoot":"","sources":["index.ts"],"names":[],"mappings":";AAAA,IAAM,CAAC,GAAG,QAAQ,CAAC,sBAAsB,CAAC,WAAW,CAAC,CAAC,CAAC,CAAC,CAAC;AAE1D,IAAM,IAAI,GAAG,QAAQ,CAAC,aAAa,CAAC,MAAM,CAAC,CAAC;AAC5C,IAAI,CAAC,WAAW,GAAG,qCAAqC,CAAC;AACzD,CAAC,aAAD,CAAC,uBAAD,CAAC,CAAE,WAAW,CAAC,IAAI,EAAE;AAErB,IAAM,MAAM,GAAG,QAAQ,CAAC,aAAa,CAAC,QAAQ,CAAC,CAAC;AAChD,MAAM,aAAN,MAAM,uBAAN,MAAM,CAAE,gBAAgB,CAAC,OAAO,EAAE;IAChC,MAAM,CAAC,KAAK,CAAC,+BAA+B,CAAC,CAAC;AAChD,CAAC,EAAE"} -------------------------------------------------------------------------------- /chapters/chapter-2_Core_Principles_and_use_cases/DOM/index.ts: -------------------------------------------------------------------------------- 1 | const p = document.getElementsByClassName("paragraph")[0]; 2 | 3 | const spanArea = document.createElement("span"); 4 | spanArea.textContent = "This is a text we added dynamically"; 5 | p?.appendChild(spanArea); 6 | 7 | const actionButton = document.querySelector("button"); 8 | actionButton?.addEventListener("click", () => { 9 | window.alert("You Clicked the Submit Button"); 10 | }); 11 | -------------------------------------------------------------------------------- /chapters/chapter-2_Core_Principles_and_use_cases/DOM/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "../../../tsconfig.json", 3 | 4 | "compilerOptions": { 5 | "lib": ["DOM"], 6 | // compiler options 7 | "composite": true, 8 | "noImplicitAny": false, 9 | "outDir": "./" 10 | } 11 | } -------------------------------------------------------------------------------- /chapters/chapter-2_Core_Principles_and_use_cases/DOM/tsconfig.tsbuildinfo: -------------------------------------------------------------------------------- 1 | { 2 | "program": { 3 | "fileInfos": { 4 | "../../node_modules/typescript/lib/lib.es5.d.ts": { 5 | "version": "9622e8bd7cc72a7dab819a8011ecbf81d443638082e5cb99ecf2e75ff56ffc9d", 6 | "signature": "9622e8bd7cc72a7dab819a8011ecbf81d443638082e5cb99ecf2e75ff56ffc9d", 7 | "affectsGlobalScope": true 8 | }, 9 | "../../node_modules/typescript/lib/lib.es2015.d.ts": { 10 | "version": "dc47c4fa66b9b9890cf076304de2a9c5201e94b740cffdf09f87296d877d71f6", 11 | "signature": "dc47c4fa66b9b9890cf076304de2a9c5201e94b740cffdf09f87296d877d71f6", 12 | "affectsGlobalScope": false 13 | }, 14 | "../../node_modules/typescript/lib/lib.es2016.d.ts": { 15 | "version": "7a387c58583dfca701b6c85e0adaf43fb17d590fb16d5b2dc0a2fbd89f35c467", 16 | "signature": "7a387c58583dfca701b6c85e0adaf43fb17d590fb16d5b2dc0a2fbd89f35c467", 17 | "affectsGlobalScope": false 18 | }, 19 | "../../node_modules/typescript/lib/lib.es2017.d.ts": { 20 | "version": "8a12173c586e95f4433e0c6dc446bc88346be73ffe9ca6eec7aa63c8f3dca7f9", 21 | "signature": "8a12173c586e95f4433e0c6dc446bc88346be73ffe9ca6eec7aa63c8f3dca7f9", 22 | "affectsGlobalScope": false 23 | }, 24 | "../../node_modules/typescript/lib/lib.es2018.d.ts": { 25 | "version": "5f4e733ced4e129482ae2186aae29fde948ab7182844c3a5a51dd346182c7b06", 26 | "signature": "5f4e733ced4e129482ae2186aae29fde948ab7182844c3a5a51dd346182c7b06", 27 | "affectsGlobalScope": false 28 | }, 29 | "../../node_modules/typescript/lib/lib.dom.d.ts": { 30 | "version": "abadddbf660adeec27e9a56584907d52fa1d6e1e1dc49f639a921baa951b7a84", 31 | "signature": "abadddbf660adeec27e9a56584907d52fa1d6e1e1dc49f639a921baa951b7a84", 32 | "affectsGlobalScope": true 33 | }, 34 | "../../node_modules/typescript/lib/lib.es2015.core.d.ts": { 35 | "version": "46ee15e9fefa913333b61eaf6b18885900b139867d89832a515059b62cf16a17", 36 | "signature": "46ee15e9fefa913333b61eaf6b18885900b139867d89832a515059b62cf16a17", 37 | "affectsGlobalScope": true 38 | }, 39 | "../../node_modules/typescript/lib/lib.es2015.collection.d.ts": { 40 | "version": "43fb1d932e4966a39a41b464a12a81899d9ae5f2c829063f5571b6b87e6d2f9c", 41 | "signature": "43fb1d932e4966a39a41b464a12a81899d9ae5f2c829063f5571b6b87e6d2f9c", 42 | "affectsGlobalScope": true 43 | }, 44 | "../../node_modules/typescript/lib/lib.es2015.generator.d.ts": { 45 | "version": "cdccba9a388c2ee3fd6ad4018c640a471a6c060e96f1232062223063b0a5ac6a", 46 | "signature": "cdccba9a388c2ee3fd6ad4018c640a471a6c060e96f1232062223063b0a5ac6a", 47 | "affectsGlobalScope": true 48 | }, 49 | "../../node_modules/typescript/lib/lib.es2015.iterable.d.ts": { 50 | "version": "8b2a5df1ce95f78f6b74f1a555ccdb6baab0486b42d8345e0871dd82811f9b9a", 51 | "signature": "8b2a5df1ce95f78f6b74f1a555ccdb6baab0486b42d8345e0871dd82811f9b9a", 52 | "affectsGlobalScope": true 53 | }, 54 | "../../node_modules/typescript/lib/lib.es2015.promise.d.ts": { 55 | "version": "2bb4b3927299434052b37851a47bf5c39764f2ba88a888a107b32262e9292b7c", 56 | "signature": "2bb4b3927299434052b37851a47bf5c39764f2ba88a888a107b32262e9292b7c", 57 | "affectsGlobalScope": true 58 | }, 59 | "../../node_modules/typescript/lib/lib.es2015.proxy.d.ts": { 60 | "version": "7207e317a2cb07a177e7d963ab7b8c0e85dde7f9ddb50351f830239bf597569e", 61 | "signature": "7207e317a2cb07a177e7d963ab7b8c0e85dde7f9ddb50351f830239bf597569e", 62 | "affectsGlobalScope": true 63 | }, 64 | "../../node_modules/typescript/lib/lib.es2015.reflect.d.ts": { 65 | "version": "cb609802a8698aa28b9c56331d4b53f590ca3c1c3a255350304ae3d06017779d", 66 | "signature": "cb609802a8698aa28b9c56331d4b53f590ca3c1c3a255350304ae3d06017779d", 67 | "affectsGlobalScope": true 68 | }, 69 | "../../node_modules/typescript/lib/lib.es2015.symbol.d.ts": { 70 | "version": "3013574108c36fd3aaca79764002b3717da09725a36a6fc02eac386593110f93", 71 | "signature": "3013574108c36fd3aaca79764002b3717da09725a36a6fc02eac386593110f93", 72 | "affectsGlobalScope": true 73 | }, 74 | "../../node_modules/typescript/lib/lib.es2015.symbol.wellknown.d.ts": { 75 | "version": "9d122b7e8c1a5c72506eea50c0973cba55b92b5532d5cafa8a6ce2c547d57551", 76 | "signature": "9d122b7e8c1a5c72506eea50c0973cba55b92b5532d5cafa8a6ce2c547d57551", 77 | "affectsGlobalScope": true 78 | }, 79 | "../../node_modules/typescript/lib/lib.es2016.array.include.d.ts": { 80 | "version": "3be5a1453daa63e031d266bf342f3943603873d890ab8b9ada95e22389389006", 81 | "signature": "3be5a1453daa63e031d266bf342f3943603873d890ab8b9ada95e22389389006", 82 | "affectsGlobalScope": true 83 | }, 84 | "../../node_modules/typescript/lib/lib.es2017.object.d.ts": { 85 | "version": "17bb1fc99591b00515502d264fa55dc8370c45c5298f4a5c2083557dccba5a2a", 86 | "signature": "17bb1fc99591b00515502d264fa55dc8370c45c5298f4a5c2083557dccba5a2a", 87 | "affectsGlobalScope": true 88 | }, 89 | "../../node_modules/typescript/lib/lib.es2017.sharedmemory.d.ts": { 90 | "version": "7ce9f0bde3307ca1f944119f6365f2d776d281a393b576a18a2f2893a2d75c98", 91 | "signature": "7ce9f0bde3307ca1f944119f6365f2d776d281a393b576a18a2f2893a2d75c98", 92 | "affectsGlobalScope": true 93 | }, 94 | "../../node_modules/typescript/lib/lib.es2017.string.d.ts": { 95 | "version": "6a6b173e739a6a99629a8594bfb294cc7329bfb7b227f12e1f7c11bc163b8577", 96 | "signature": "6a6b173e739a6a99629a8594bfb294cc7329bfb7b227f12e1f7c11bc163b8577", 97 | "affectsGlobalScope": true 98 | }, 99 | "../../node_modules/typescript/lib/lib.es2017.intl.d.ts": { 100 | "version": "12a310447c5d23c7d0d5ca2af606e3bd08afda69100166730ab92c62999ebb9d", 101 | "signature": "12a310447c5d23c7d0d5ca2af606e3bd08afda69100166730ab92c62999ebb9d", 102 | "affectsGlobalScope": true 103 | }, 104 | "../../node_modules/typescript/lib/lib.es2017.typedarrays.d.ts": { 105 | "version": "b0124885ef82641903d232172577f2ceb5d3e60aed4da1153bab4221e1f6dd4e", 106 | "signature": "b0124885ef82641903d232172577f2ceb5d3e60aed4da1153bab4221e1f6dd4e", 107 | "affectsGlobalScope": true 108 | }, 109 | "../../node_modules/typescript/lib/lib.es2018.asyncgenerator.d.ts": { 110 | "version": "0eb85d6c590b0d577919a79e0084fa1744c1beba6fd0d4e951432fa1ede5510a", 111 | "signature": "0eb85d6c590b0d577919a79e0084fa1744c1beba6fd0d4e951432fa1ede5510a", 112 | "affectsGlobalScope": true 113 | }, 114 | "../../node_modules/typescript/lib/lib.es2018.asynciterable.d.ts": { 115 | "version": "a40c4d82bf13fcded295ac29f354eb7d40249613c15e07b53f2fc75e45e16359", 116 | "signature": "a40c4d82bf13fcded295ac29f354eb7d40249613c15e07b53f2fc75e45e16359", 117 | "affectsGlobalScope": true 118 | }, 119 | "../../node_modules/typescript/lib/lib.es2018.intl.d.ts": { 120 | "version": "df9c8a72ca8b0ed62f5470b41208a0587f0f73f0a7db28e5a1272cf92537518e", 121 | "signature": "df9c8a72ca8b0ed62f5470b41208a0587f0f73f0a7db28e5a1272cf92537518e", 122 | "affectsGlobalScope": true 123 | }, 124 | "../../node_modules/typescript/lib/lib.es2018.promise.d.ts": { 125 | "version": "bb2d3fb05a1d2ffbca947cc7cbc95d23e1d053d6595391bd325deb265a18d36c", 126 | "signature": "bb2d3fb05a1d2ffbca947cc7cbc95d23e1d053d6595391bd325deb265a18d36c", 127 | "affectsGlobalScope": true 128 | }, 129 | "../../node_modules/typescript/lib/lib.es2018.regexp.d.ts": { 130 | "version": "c80df75850fea5caa2afe43b9949338ce4e2de086f91713e9af1a06f973872b8", 131 | "signature": "c80df75850fea5caa2afe43b9949338ce4e2de086f91713e9af1a06f973872b8", 132 | "affectsGlobalScope": true 133 | }, 134 | "../../node_modules/typescript/lib/lib.es2020.bigint.d.ts": { 135 | "version": "7b5a10e3c897fabece5a51aa85b4111727d7adb53c2734b5d37230ff96802a09", 136 | "signature": "7b5a10e3c897fabece5a51aa85b4111727d7adb53c2734b5d37230ff96802a09", 137 | "affectsGlobalScope": true 138 | }, 139 | "../../node_modules/typescript/lib/lib.esnext.intl.d.ts": { 140 | "version": "89bf2b7a601b73ea4311eda9c41f86a58994fec1bee3b87c4a14d68d9adcdcbd", 141 | "signature": "89bf2b7a601b73ea4311eda9c41f86a58994fec1bee3b87c4a14d68d9adcdcbd", 142 | "affectsGlobalScope": true 143 | }, 144 | "./index.ts": { 145 | "version": "79f6a3aa3889c38247d7e8668c50fe6acb1acf7470c6c2ba973c1054277f0489", 146 | "signature": "c9e5f09ba6c9bcb7d8627cd56874cfbdc95a91f66f39dab5db02906ea5a94dfe", 147 | "affectsGlobalScope": true 148 | }, 149 | "../../node_modules/@babel/types/lib/index.d.ts": { 150 | "version": "7c6b4c3bbc2c3cebe5cc1364e84a4fd0cbda5fda186c0ba15c2f693f403479d2", 151 | "signature": "7c6b4c3bbc2c3cebe5cc1364e84a4fd0cbda5fda186c0ba15c2f693f403479d2", 152 | "affectsGlobalScope": false 153 | }, 154 | "../../node_modules/@types/babel__generator/index.d.ts": { 155 | "version": "b25c5f2970d06c729f464c0aeaa64b1a5b5f1355aa93554bb5f9c199b8624b1e", 156 | "signature": "b25c5f2970d06c729f464c0aeaa64b1a5b5f1355aa93554bb5f9c199b8624b1e", 157 | "affectsGlobalScope": false 158 | }, 159 | "../../node_modules/@types/babel__traverse/index.d.ts": { 160 | "version": "8a278bfba7b081cd849434c1130655046639ae90617a682436ed6954e2b57403", 161 | "signature": "8a278bfba7b081cd849434c1130655046639ae90617a682436ed6954e2b57403", 162 | "affectsGlobalScope": false 163 | }, 164 | "../../node_modules/@babel/parser/typings/babel-parser.d.ts": { 165 | "version": "6da9e714516d081abaa435946d77c8d74dba888530412dc71601e83d2c8a512e", 166 | "signature": "6da9e714516d081abaa435946d77c8d74dba888530412dc71601e83d2c8a512e", 167 | "affectsGlobalScope": false 168 | }, 169 | "../../node_modules/@types/babel__template/index.d.ts": { 170 | "version": "3051751533eee92572241b3cef28333212401408c4e7aa21718714b793c0f4ed", 171 | "signature": "3051751533eee92572241b3cef28333212401408c4e7aa21718714b793c0f4ed", 172 | "affectsGlobalScope": false 173 | }, 174 | "../../node_modules/@types/babel__core/index.d.ts": { 175 | "version": "a66e700ed470a0cb52d14f3376c1605c70fec8e9659e45f7e22ad07fcd06ae04", 176 | "signature": "a66e700ed470a0cb52d14f3376c1605c70fec8e9659e45f7e22ad07fcd06ae04", 177 | "affectsGlobalScope": false 178 | }, 179 | "../../node_modules/@types/node/globals.d.ts": { 180 | "version": "215d8d9a2c480fd460127edc048d68d9931d3b27f95132253a6e71975f060bb1", 181 | "signature": "215d8d9a2c480fd460127edc048d68d9931d3b27f95132253a6e71975f060bb1", 182 | "affectsGlobalScope": true 183 | }, 184 | "../../node_modules/@types/node/async_hooks.d.ts": { 185 | "version": "c438b413e94ff76dfa20ae005f33a1c84f2480d1d66e0fd687501020d0de9b50", 186 | "signature": "c438b413e94ff76dfa20ae005f33a1c84f2480d1d66e0fd687501020d0de9b50", 187 | "affectsGlobalScope": false 188 | }, 189 | "../../node_modules/@types/node/buffer.d.ts": { 190 | "version": "bc6a78961535181265845bf9b9e8a147ffd0ca275097ceb670a9b92afa825152", 191 | "signature": "bc6a78961535181265845bf9b9e8a147ffd0ca275097ceb670a9b92afa825152", 192 | "affectsGlobalScope": false 193 | }, 194 | "../../node_modules/@types/node/child_process.d.ts": { 195 | "version": "a4b5411d87995b9fb847f491f4388b38f829d8ab39be3e2fd65a3e40709f29a8", 196 | "signature": "a4b5411d87995b9fb847f491f4388b38f829d8ab39be3e2fd65a3e40709f29a8", 197 | "affectsGlobalScope": false 198 | }, 199 | "../../node_modules/@types/node/cluster.d.ts": { 200 | "version": "123ec69e4b3a686eb49afd94ebe3292a5c84a867ecbcb6bb84bdd720a12af803", 201 | "signature": "123ec69e4b3a686eb49afd94ebe3292a5c84a867ecbcb6bb84bdd720a12af803", 202 | "affectsGlobalScope": false 203 | }, 204 | "../../node_modules/@types/node/console.d.ts": { 205 | "version": "eb5197aade83cb0e360ac407289c53a8009e8fdae7939892a0240d30444496b6", 206 | "signature": "eb5197aade83cb0e360ac407289c53a8009e8fdae7939892a0240d30444496b6", 207 | "affectsGlobalScope": true 208 | }, 209 | "../../node_modules/@types/node/constants.d.ts": { 210 | "version": "90c85ddbb8de82cd19198bda062065fc51b7407c0f206f2e399e65a52e979720", 211 | "signature": "90c85ddbb8de82cd19198bda062065fc51b7407c0f206f2e399e65a52e979720", 212 | "affectsGlobalScope": false 213 | }, 214 | "../../node_modules/@types/node/crypto.d.ts": { 215 | "version": "3d9c3ccc05ebc6e288bef007bcc47a2fc0dce748ea634093ef0732b9be743805", 216 | "signature": "3d9c3ccc05ebc6e288bef007bcc47a2fc0dce748ea634093ef0732b9be743805", 217 | "affectsGlobalScope": false 218 | }, 219 | "../../node_modules/@types/node/dgram.d.ts": { 220 | "version": "7e050b767ed10c7ffbc01f314defbf420bf0b5d54ce666e1c87507c035dfc191", 221 | "signature": "7e050b767ed10c7ffbc01f314defbf420bf0b5d54ce666e1c87507c035dfc191", 222 | "affectsGlobalScope": false 223 | }, 224 | "../../node_modules/@types/node/dns.d.ts": { 225 | "version": "51f82a7e0819a91c170eb1ad39ad1917a0cbb8f9354fdaed66a5189745f63f71", 226 | "signature": "51f82a7e0819a91c170eb1ad39ad1917a0cbb8f9354fdaed66a5189745f63f71", 227 | "affectsGlobalScope": false 228 | }, 229 | "../../node_modules/@types/node/domain.d.ts": { 230 | "version": "2866a528b2708aa272ec3eaafd3c980abb23aec1ef831cfc5eb2186b98c37ce5", 231 | "signature": "2866a528b2708aa272ec3eaafd3c980abb23aec1ef831cfc5eb2186b98c37ce5", 232 | "affectsGlobalScope": true 233 | }, 234 | "../../node_modules/@types/node/events.d.ts": { 235 | "version": "153d835dc32985120790e10102834b0a5bd979bb5e42bfbb33c0ff6260cf03ce", 236 | "signature": "153d835dc32985120790e10102834b0a5bd979bb5e42bfbb33c0ff6260cf03ce", 237 | "affectsGlobalScope": true 238 | }, 239 | "../../node_modules/@types/node/fs.d.ts": { 240 | "version": "2e204fb437b8e98937c933cb7e739b24b871872161997d7dfe98841b861a14f0", 241 | "signature": "2e204fb437b8e98937c933cb7e739b24b871872161997d7dfe98841b861a14f0", 242 | "affectsGlobalScope": false 243 | }, 244 | "../../node_modules/@types/node/fs/promises.d.ts": { 245 | "version": "1c0692002778565d9b3ee0edea6ca133caadd714597228525fec8e2fd9846755", 246 | "signature": "1c0692002778565d9b3ee0edea6ca133caadd714597228525fec8e2fd9846755", 247 | "affectsGlobalScope": false 248 | }, 249 | "../../node_modules/@types/node/http.d.ts": { 250 | "version": "f2f8a385694fd71a421616cbaca477a539f30f4098f11261b1d188d72dc3478a", 251 | "signature": "f2f8a385694fd71a421616cbaca477a539f30f4098f11261b1d188d72dc3478a", 252 | "affectsGlobalScope": false 253 | }, 254 | "../../node_modules/@types/node/http2.d.ts": { 255 | "version": "f15f1f1104aaf47d25124de81949375416e3f8ee704e3d221bea339e35421edb", 256 | "signature": "f15f1f1104aaf47d25124de81949375416e3f8ee704e3d221bea339e35421edb", 257 | "affectsGlobalScope": false 258 | }, 259 | "../../node_modules/@types/node/https.d.ts": { 260 | "version": "c969bf4c7cdfe4d5dd28aa09432f99d09ad1d8d8b839959646579521d0467d1a", 261 | "signature": "c969bf4c7cdfe4d5dd28aa09432f99d09ad1d8d8b839959646579521d0467d1a", 262 | "affectsGlobalScope": false 263 | }, 264 | "../../node_modules/@types/node/inspector.d.ts": { 265 | "version": "6c3857edaeeaaf43812f527830ebeece9266b6e8eb5271ab6d2f0008306c9947", 266 | "signature": "6c3857edaeeaaf43812f527830ebeece9266b6e8eb5271ab6d2f0008306c9947", 267 | "affectsGlobalScope": false 268 | }, 269 | "../../node_modules/@types/node/module.d.ts": { 270 | "version": "bc6a77e750f4d34584e46b1405b771fb69a224197dd6bafe5b0392a29a70b665", 271 | "signature": "bc6a77e750f4d34584e46b1405b771fb69a224197dd6bafe5b0392a29a70b665", 272 | "affectsGlobalScope": false 273 | }, 274 | "../../node_modules/@types/node/net.d.ts": { 275 | "version": "3c5edfced8c5cd35e3680789deabbe4f478b67a42b26d82d91eea9d3f07f4771", 276 | "signature": "3c5edfced8c5cd35e3680789deabbe4f478b67a42b26d82d91eea9d3f07f4771", 277 | "affectsGlobalScope": false 278 | }, 279 | "../../node_modules/@types/node/os.d.ts": { 280 | "version": "ed4ae81196cccc10f297d228bca8d02e31058e6d723a3c5bc4be5fb3c61c6a34", 281 | "signature": "ed4ae81196cccc10f297d228bca8d02e31058e6d723a3c5bc4be5fb3c61c6a34", 282 | "affectsGlobalScope": false 283 | }, 284 | "../../node_modules/@types/node/path.d.ts": { 285 | "version": "84044697c8b3e08ef24e4b32cfe6440143d07e469a5e34bda0635276d32d9f35", 286 | "signature": "84044697c8b3e08ef24e4b32cfe6440143d07e469a5e34bda0635276d32d9f35", 287 | "affectsGlobalScope": false 288 | }, 289 | "../../node_modules/@types/node/perf_hooks.d.ts": { 290 | "version": "4982d94cb6427263c8839d8d6324a8bbe129e931deb61a7380f8fad17ba2cfc0", 291 | "signature": "4982d94cb6427263c8839d8d6324a8bbe129e931deb61a7380f8fad17ba2cfc0", 292 | "affectsGlobalScope": false 293 | }, 294 | "../../node_modules/@types/node/process.d.ts": { 295 | "version": "ae4bc1dd4d9de7bbea6ce419db45af82a81358e6014c9e1235b5d252e06f8ab8", 296 | "signature": "ae4bc1dd4d9de7bbea6ce419db45af82a81358e6014c9e1235b5d252e06f8ab8", 297 | "affectsGlobalScope": true 298 | }, 299 | "../../node_modules/@types/node/punycode.d.ts": { 300 | "version": "3f6a1fd73c9dc3bd7f4b79bc075297ca6527904df69b0f2c2c94e4c4c7d9a32c", 301 | "signature": "3f6a1fd73c9dc3bd7f4b79bc075297ca6527904df69b0f2c2c94e4c4c7d9a32c", 302 | "affectsGlobalScope": false 303 | }, 304 | "../../node_modules/@types/node/querystring.d.ts": { 305 | "version": "884560fda6c3868f925f022adc3a1289fe6507bbb45adb10fa1bbcc73a941bb0", 306 | "signature": "884560fda6c3868f925f022adc3a1289fe6507bbb45adb10fa1bbcc73a941bb0", 307 | "affectsGlobalScope": false 308 | }, 309 | "../../node_modules/@types/node/readline.d.ts": { 310 | "version": "6b2bb67b0942bcfce93e1d6fad5f70afd54940a2b13df7f311201fba54b2cbe9", 311 | "signature": "6b2bb67b0942bcfce93e1d6fad5f70afd54940a2b13df7f311201fba54b2cbe9", 312 | "affectsGlobalScope": false 313 | }, 314 | "../../node_modules/@types/node/repl.d.ts": { 315 | "version": "acbed967a379b3e9f73237ba9473f8b337eeea14b7dc64d445430b5d695751da", 316 | "signature": "acbed967a379b3e9f73237ba9473f8b337eeea14b7dc64d445430b5d695751da", 317 | "affectsGlobalScope": false 318 | }, 319 | "../../node_modules/@types/node/stream.d.ts": { 320 | "version": "e7b5a3f40f19d9eea71890c70dfb37ac5dd82cbffe5f95bc8f23c536455732d0", 321 | "signature": "e7b5a3f40f19d9eea71890c70dfb37ac5dd82cbffe5f95bc8f23c536455732d0", 322 | "affectsGlobalScope": false 323 | }, 324 | "../../node_modules/@types/node/string_decoder.d.ts": { 325 | "version": "d67e08745494b000da9410c1ae2fdc9965fc6d593fe0f381a47491f75417d457", 326 | "signature": "d67e08745494b000da9410c1ae2fdc9965fc6d593fe0f381a47491f75417d457", 327 | "affectsGlobalScope": false 328 | }, 329 | "../../node_modules/@types/node/timers.d.ts": { 330 | "version": "b40652bf8ce4a18133b31349086523b219724dca8df3448c1a0742528e7ad5b9", 331 | "signature": "b40652bf8ce4a18133b31349086523b219724dca8df3448c1a0742528e7ad5b9", 332 | "affectsGlobalScope": false 333 | }, 334 | "../../node_modules/@types/node/tls.d.ts": { 335 | "version": "01c351e71743ff26450789ec4ca6a1421c647c9c459aa8007e9e1914073b5db2", 336 | "signature": "01c351e71743ff26450789ec4ca6a1421c647c9c459aa8007e9e1914073b5db2", 337 | "affectsGlobalScope": false 338 | }, 339 | "../../node_modules/@types/node/trace_events.d.ts": { 340 | "version": "a77fdb357c78b70142b2fdbbfb72958d69e8f765fd2a3c69946c1018e89d4638", 341 | "signature": "a77fdb357c78b70142b2fdbbfb72958d69e8f765fd2a3c69946c1018e89d4638", 342 | "affectsGlobalScope": false 343 | }, 344 | "../../node_modules/@types/node/tty.d.ts": { 345 | "version": "3c2ac350c3baa61fd2b1925844109e098f4376d0768a4643abc82754fd752748", 346 | "signature": "3c2ac350c3baa61fd2b1925844109e098f4376d0768a4643abc82754fd752748", 347 | "affectsGlobalScope": false 348 | }, 349 | "../../node_modules/@types/node/url.d.ts": { 350 | "version": "4001971b90f18161f7bd46d68150d32a5da47e3f177956a267a2b29a2771896a", 351 | "signature": "4001971b90f18161f7bd46d68150d32a5da47e3f177956a267a2b29a2771896a", 352 | "affectsGlobalScope": false 353 | }, 354 | "../../node_modules/@types/node/util.d.ts": { 355 | "version": "f28704c27e1bd58068052f5407541a3306f0ffc77f068e2891f46527a4569e25", 356 | "signature": "f28704c27e1bd58068052f5407541a3306f0ffc77f068e2891f46527a4569e25", 357 | "affectsGlobalScope": false 358 | }, 359 | "../../node_modules/@types/node/v8.d.ts": { 360 | "version": "289be113bad7ee27ee7fa5b1e373c964c9789a5e9ed7db5ddcb631371120b953", 361 | "signature": "289be113bad7ee27ee7fa5b1e373c964c9789a5e9ed7db5ddcb631371120b953", 362 | "affectsGlobalScope": false 363 | }, 364 | "../../node_modules/@types/node/vm.d.ts": { 365 | "version": "baf0b82ffc5d2616f44a6fb1f81e8d798545bebf0c30f5d8b003a1dba1acfb3f", 366 | "signature": "baf0b82ffc5d2616f44a6fb1f81e8d798545bebf0c30f5d8b003a1dba1acfb3f", 367 | "affectsGlobalScope": false 368 | }, 369 | "../../node_modules/@types/node/worker_threads.d.ts": { 370 | "version": "c6a5b34f1e725019445754f1e733585f113e0dced75f137bd3c4af5853d3f6ab", 371 | "signature": "c6a5b34f1e725019445754f1e733585f113e0dced75f137bd3c4af5853d3f6ab", 372 | "affectsGlobalScope": false 373 | }, 374 | "../../node_modules/@types/node/zlib.d.ts": { 375 | "version": "15fbe50526244954eb2f933546bca6cdcf0db16c9428d099b3b386c1db5799ab", 376 | "signature": "15fbe50526244954eb2f933546bca6cdcf0db16c9428d099b3b386c1db5799ab", 377 | "affectsGlobalScope": false 378 | }, 379 | "../../node_modules/@types/node/ts3.4/base.d.ts": { 380 | "version": "d44028ae0127eb3e9fcfa5f55a8b81d64775ce15aca1020fe25c511bbb055834", 381 | "signature": "d44028ae0127eb3e9fcfa5f55a8b81d64775ce15aca1020fe25c511bbb055834", 382 | "affectsGlobalScope": false 383 | }, 384 | "../../node_modules/@types/node/globals.global.d.ts": { 385 | "version": "2708349d5a11a5c2e5f3a0765259ebe7ee00cdcc8161cb9990cb4910328442a1", 386 | "signature": "2708349d5a11a5c2e5f3a0765259ebe7ee00cdcc8161cb9990cb4910328442a1", 387 | "affectsGlobalScope": true 388 | }, 389 | "../../node_modules/@types/node/wasi.d.ts": { 390 | "version": "4e0a4d84b15692ea8669fe4f3d05a4f204567906b1347da7a58b75f45bae48d3", 391 | "signature": "4e0a4d84b15692ea8669fe4f3d05a4f204567906b1347da7a58b75f45bae48d3", 392 | "affectsGlobalScope": false 393 | }, 394 | "../../node_modules/@types/node/ts3.6/base.d.ts": { 395 | "version": "ad1ae5ae98eceb9af99061e83e867b9897d267aebc8f3b938c9424deabadf4bb", 396 | "signature": "ad1ae5ae98eceb9af99061e83e867b9897d267aebc8f3b938c9424deabadf4bb", 397 | "affectsGlobalScope": false 398 | }, 399 | "../../node_modules/@types/node/assert.d.ts": { 400 | "version": "b3593bd345ebea5e4d0a894c03251a3774b34df3d6db57075c18e089a599ba76", 401 | "signature": "b3593bd345ebea5e4d0a894c03251a3774b34df3d6db57075c18e089a599ba76", 402 | "affectsGlobalScope": false 403 | }, 404 | "../../node_modules/@types/node/base.d.ts": { 405 | "version": "e61a21e9418f279bc480394a94d1581b2dee73747adcbdef999b6737e34d721b", 406 | "signature": "e61a21e9418f279bc480394a94d1581b2dee73747adcbdef999b6737e34d721b", 407 | "affectsGlobalScope": false 408 | }, 409 | "../../node_modules/@types/node/index.d.ts": { 410 | "version": "744e6430bafc6f39f66c4fc1e6a0d8c9551260ffd1782aa7e3f5166ac6aa1f86", 411 | "signature": "744e6430bafc6f39f66c4fc1e6a0d8c9551260ffd1782aa7e3f5166ac6aa1f86", 412 | "affectsGlobalScope": false 413 | }, 414 | "../../node_modules/@types/graceful-fs/index.d.ts": { 415 | "version": "3ebae8c00411116a66fca65b08228ea0cf0b72724701f9b854442100aab55aba", 416 | "signature": "3ebae8c00411116a66fca65b08228ea0cf0b72724701f9b854442100aab55aba", 417 | "affectsGlobalScope": false 418 | }, 419 | "../../node_modules/@types/istanbul-lib-coverage/index.d.ts": { 420 | "version": "de18acda71730bac52f4b256ce7511bb56cc21f6f114c59c46782eff2f632857", 421 | "signature": "de18acda71730bac52f4b256ce7511bb56cc21f6f114c59c46782eff2f632857", 422 | "affectsGlobalScope": false 423 | }, 424 | "../../node_modules/@types/istanbul-lib-report/index.d.ts": { 425 | "version": "7eb06594824ada538b1d8b48c3925a83e7db792f47a081a62cf3e5c4e23cf0ee", 426 | "signature": "7eb06594824ada538b1d8b48c3925a83e7db792f47a081a62cf3e5c4e23cf0ee", 427 | "affectsGlobalScope": false 428 | }, 429 | "../../node_modules/@types/istanbul-reports/index.d.ts": { 430 | "version": "905c3e8f7ddaa6c391b60c05b2f4c3931d7127ad717a080359db3df510b7bdab", 431 | "signature": "905c3e8f7ddaa6c391b60c05b2f4c3931d7127ad717a080359db3df510b7bdab", 432 | "affectsGlobalScope": false 433 | }, 434 | "../../node_modules/jest-diff/build/cleanupsemantic.d.ts": { 435 | "version": "d8aab31ba8e618cc3eea10b0945de81cb93b7e8150a013a482332263b9305322", 436 | "signature": "d8aab31ba8e618cc3eea10b0945de81cb93b7e8150a013a482332263b9305322", 437 | "affectsGlobalScope": false 438 | }, 439 | "../../node_modules/jest-diff/build/types.d.ts": { 440 | "version": "69da61a7b5093dac77fa3bec8be95dcf9a74c95a0e9161edb98bb24e30e439d2", 441 | "signature": "69da61a7b5093dac77fa3bec8be95dcf9a74c95a0e9161edb98bb24e30e439d2", 442 | "affectsGlobalScope": false 443 | }, 444 | "../../node_modules/jest-diff/build/difflines.d.ts": { 445 | "version": "561eca7a381b96d6ccac6e4061e6d2ae53f5bc44203f3fd9f5b26864c32ae6e9", 446 | "signature": "561eca7a381b96d6ccac6e4061e6d2ae53f5bc44203f3fd9f5b26864c32ae6e9", 447 | "affectsGlobalScope": false 448 | }, 449 | "../../node_modules/jest-diff/build/printdiffs.d.ts": { 450 | "version": "62ea38627e3ebab429f7616812a9394d327c2bc271003dfba985de9b4137369f", 451 | "signature": "62ea38627e3ebab429f7616812a9394d327c2bc271003dfba985de9b4137369f", 452 | "affectsGlobalScope": false 453 | }, 454 | "../../node_modules/jest-diff/build/index.d.ts": { 455 | "version": "b4439890c168d646357928431100daac5cbdee1d345a34e6bf6eca9f3abe22bc", 456 | "signature": "b4439890c168d646357928431100daac5cbdee1d345a34e6bf6eca9f3abe22bc", 457 | "affectsGlobalScope": false 458 | }, 459 | "../../node_modules/pretty-format/build/types.d.ts": { 460 | "version": "5d72971a459517c44c1379dab9ed248e87a61ba0a1e0f25c9d67e1e640cd9a09", 461 | "signature": "5d72971a459517c44c1379dab9ed248e87a61ba0a1e0f25c9d67e1e640cd9a09", 462 | "affectsGlobalScope": false 463 | }, 464 | "../../node_modules/pretty-format/build/index.d.ts": { 465 | "version": "02d734976af36f4273d930bea88b3e62adf6b078cf120c1c63d49aa8d8427c5c", 466 | "signature": "02d734976af36f4273d930bea88b3e62adf6b078cf120c1c63d49aa8d8427c5c", 467 | "affectsGlobalScope": false 468 | }, 469 | "../../node_modules/@types/jest/index.d.ts": { 470 | "version": "71f30fba971582dc744373cbc8b06c1eb64dc24a6ccbc9b457f94fb68c67cb4e", 471 | "signature": "71f30fba971582dc744373cbc8b06c1eb64dc24a6ccbc9b457f94fb68c67cb4e", 472 | "affectsGlobalScope": true 473 | }, 474 | "../../node_modules/@types/normalize-package-data/index.d.ts": { 475 | "version": "c9ad058b2cc9ce6dc2ed92960d6d009e8c04bef46d3f5312283debca6869f613", 476 | "signature": "c9ad058b2cc9ce6dc2ed92960d6d009e8c04bef46d3f5312283debca6869f613", 477 | "affectsGlobalScope": false 478 | }, 479 | "../../node_modules/@types/prettier/index.d.ts": { 480 | "version": "d916cd19ddfd778aaa1dbfad1adbc5cddf5172b42cbc85964084556ea5eaace5", 481 | "signature": "d916cd19ddfd778aaa1dbfad1adbc5cddf5172b42cbc85964084556ea5eaace5", 482 | "affectsGlobalScope": false 483 | }, 484 | "../../node_modules/@types/stack-utils/index.d.ts": { 485 | "version": "c6c4fea9acc55d5e38ff2b70d57ab0b5cdbd08f8bc5d7a226e322cea128c5b57", 486 | "signature": "c6c4fea9acc55d5e38ff2b70d57ab0b5cdbd08f8bc5d7a226e322cea128c5b57", 487 | "affectsGlobalScope": false 488 | }, 489 | "../../node_modules/@types/yargs-parser/index.d.ts": { 490 | "version": "3bdd93ec24853e61bfa4c63ebaa425ff3e474156e87a47d90122e1d8cc717c1f", 491 | "signature": "3bdd93ec24853e61bfa4c63ebaa425ff3e474156e87a47d90122e1d8cc717c1f", 492 | "affectsGlobalScope": false 493 | }, 494 | "../../node_modules/@types/yargs/index.d.ts": { 495 | "version": "5a2a25feca554a8f289ed62114771b8c63d89f2b58325e2f8b7043e4e0160d11", 496 | "signature": "5a2a25feca554a8f289ed62114771b8c63d89f2b58325e2f8b7043e4e0160d11", 497 | "affectsGlobalScope": false 498 | } 499 | }, 500 | "options": { 501 | "target": 1, 502 | "module": 1, 503 | "lib": [ 504 | "lib.dom.d.ts" 505 | ], 506 | "sourceMap": true, 507 | "outDir": "./", 508 | "downlevelIteration": true, 509 | "strict": true, 510 | "noImplicitAny": false, 511 | "strictNullChecks": true, 512 | "esModuleInterop": true, 513 | "experimentalDecorators": true, 514 | "emitDecoratorMetadata": true, 515 | "skipLibCheck": true, 516 | "forceConsistentCasingInFileNames": true, 517 | "composite": true, 518 | "project": "./tsconfig.json", 519 | "configFilePath": "./tsconfig.json" 520 | }, 521 | "referencedMap": { 522 | "../../node_modules/@babel/parser/typings/babel-parser.d.ts": [ 523 | "../../node_modules/@babel/types/lib/index.d.ts" 524 | ], 525 | "../../node_modules/@types/babel__core/index.d.ts": [ 526 | "../../node_modules/@babel/parser/typings/babel-parser.d.ts", 527 | "../../node_modules/@babel/types/lib/index.d.ts", 528 | "../../node_modules/@types/babel__generator/index.d.ts", 529 | "../../node_modules/@types/babel__template/index.d.ts", 530 | "../../node_modules/@types/babel__traverse/index.d.ts" 531 | ], 532 | "../../node_modules/@types/babel__generator/index.d.ts": [ 533 | "../../node_modules/@babel/types/lib/index.d.ts" 534 | ], 535 | "../../node_modules/@types/babel__template/index.d.ts": [ 536 | "../../node_modules/@babel/parser/typings/babel-parser.d.ts", 537 | "../../node_modules/@babel/types/lib/index.d.ts" 538 | ], 539 | "../../node_modules/@types/babel__traverse/index.d.ts": [ 540 | "../../node_modules/@babel/types/lib/index.d.ts" 541 | ], 542 | "../../node_modules/@types/graceful-fs/index.d.ts": [ 543 | "../../node_modules/@types/node/fs.d.ts", 544 | "../../node_modules/@types/node/index.d.ts" 545 | ], 546 | "../../node_modules/@types/istanbul-lib-report/index.d.ts": [ 547 | "../../node_modules/@types/istanbul-lib-coverage/index.d.ts" 548 | ], 549 | "../../node_modules/@types/istanbul-reports/index.d.ts": [ 550 | "../../node_modules/@types/istanbul-lib-report/index.d.ts" 551 | ], 552 | "../../node_modules/@types/jest/index.d.ts": [ 553 | "../../node_modules/jest-diff/build/index.d.ts", 554 | "../../node_modules/pretty-format/build/index.d.ts" 555 | ], 556 | "../../node_modules/@types/node/base.d.ts": [ 557 | "../../node_modules/@types/node/assert.d.ts", 558 | "../../node_modules/@types/node/ts3.6/base.d.ts" 559 | ], 560 | "../../node_modules/@types/node/child_process.d.ts": [ 561 | "../../node_modules/@types/node/events.d.ts", 562 | "../../node_modules/@types/node/fs.d.ts", 563 | "../../node_modules/@types/node/net.d.ts", 564 | "../../node_modules/@types/node/stream.d.ts" 565 | ], 566 | "../../node_modules/@types/node/cluster.d.ts": [ 567 | "../../node_modules/@types/node/child_process.d.ts", 568 | "../../node_modules/@types/node/events.d.ts", 569 | "../../node_modules/@types/node/net.d.ts" 570 | ], 571 | "../../node_modules/@types/node/console.d.ts": [ 572 | "../../node_modules/@types/node/util.d.ts" 573 | ], 574 | "../../node_modules/@types/node/constants.d.ts": [ 575 | "../../node_modules/@types/node/crypto.d.ts", 576 | "../../node_modules/@types/node/fs.d.ts", 577 | "../../node_modules/@types/node/os.d.ts" 578 | ], 579 | "../../node_modules/@types/node/crypto.d.ts": [ 580 | "../../node_modules/@types/node/stream.d.ts" 581 | ], 582 | "../../node_modules/@types/node/dgram.d.ts": [ 583 | "../../node_modules/@types/node/dns.d.ts", 584 | "../../node_modules/@types/node/events.d.ts", 585 | "../../node_modules/@types/node/net.d.ts" 586 | ], 587 | "../../node_modules/@types/node/domain.d.ts": [ 588 | "../../node_modules/@types/node/events.d.ts" 589 | ], 590 | "../../node_modules/@types/node/events.d.ts": [ 591 | "../../node_modules/@types/node/events.d.ts" 592 | ], 593 | "../../node_modules/@types/node/fs.d.ts": [ 594 | "../../node_modules/@types/node/events.d.ts", 595 | "../../node_modules/@types/node/fs/promises.d.ts", 596 | "../../node_modules/@types/node/stream.d.ts", 597 | "../../node_modules/@types/node/url.d.ts" 598 | ], 599 | "../../node_modules/@types/node/fs/promises.d.ts": [ 600 | "../../node_modules/@types/node/fs.d.ts" 601 | ], 602 | "../../node_modules/@types/node/http.d.ts": [ 603 | "../../node_modules/@types/node/net.d.ts", 604 | "../../node_modules/@types/node/stream.d.ts", 605 | "../../node_modules/@types/node/url.d.ts" 606 | ], 607 | "../../node_modules/@types/node/http2.d.ts": [ 608 | "../../node_modules/@types/node/events.d.ts", 609 | "../../node_modules/@types/node/fs.d.ts", 610 | "../../node_modules/@types/node/http.d.ts", 611 | "../../node_modules/@types/node/net.d.ts", 612 | "../../node_modules/@types/node/stream.d.ts", 613 | "../../node_modules/@types/node/tls.d.ts", 614 | "../../node_modules/@types/node/url.d.ts" 615 | ], 616 | "../../node_modules/@types/node/https.d.ts": [ 617 | "../../node_modules/@types/node/events.d.ts", 618 | "../../node_modules/@types/node/http.d.ts", 619 | "../../node_modules/@types/node/tls.d.ts", 620 | "../../node_modules/@types/node/url.d.ts" 621 | ], 622 | "../../node_modules/@types/node/index.d.ts": [ 623 | "../../node_modules/@types/node/base.d.ts" 624 | ], 625 | "../../node_modules/@types/node/inspector.d.ts": [ 626 | "../../node_modules/@types/node/events.d.ts" 627 | ], 628 | "../../node_modules/@types/node/module.d.ts": [ 629 | "../../node_modules/@types/node/url.d.ts" 630 | ], 631 | "../../node_modules/@types/node/net.d.ts": [ 632 | "../../node_modules/@types/node/dns.d.ts", 633 | "../../node_modules/@types/node/events.d.ts", 634 | "../../node_modules/@types/node/stream.d.ts" 635 | ], 636 | "../../node_modules/@types/node/perf_hooks.d.ts": [ 637 | "../../node_modules/@types/node/async_hooks.d.ts" 638 | ], 639 | "../../node_modules/@types/node/process.d.ts": [ 640 | "../../node_modules/@types/node/tty.d.ts" 641 | ], 642 | "../../node_modules/@types/node/readline.d.ts": [ 643 | "../../node_modules/@types/node/events.d.ts", 644 | "../../node_modules/@types/node/stream.d.ts" 645 | ], 646 | "../../node_modules/@types/node/repl.d.ts": [ 647 | "../../node_modules/@types/node/readline.d.ts", 648 | "../../node_modules/@types/node/util.d.ts", 649 | "../../node_modules/@types/node/vm.d.ts" 650 | ], 651 | "../../node_modules/@types/node/stream.d.ts": [ 652 | "../../node_modules/@types/node/events.d.ts" 653 | ], 654 | "../../node_modules/@types/node/tls.d.ts": [ 655 | "../../node_modules/@types/node/crypto.d.ts", 656 | "../../node_modules/@types/node/dns.d.ts", 657 | "../../node_modules/@types/node/net.d.ts", 658 | "../../node_modules/@types/node/stream.d.ts" 659 | ], 660 | "../../node_modules/@types/node/ts3.4/base.d.ts": [ 661 | "../../node_modules/@types/node/async_hooks.d.ts", 662 | "../../node_modules/@types/node/buffer.d.ts", 663 | "../../node_modules/@types/node/child_process.d.ts", 664 | "../../node_modules/@types/node/cluster.d.ts", 665 | "../../node_modules/@types/node/console.d.ts", 666 | "../../node_modules/@types/node/constants.d.ts", 667 | "../../node_modules/@types/node/crypto.d.ts", 668 | "../../node_modules/@types/node/dgram.d.ts", 669 | "../../node_modules/@types/node/dns.d.ts", 670 | "../../node_modules/@types/node/domain.d.ts", 671 | "../../node_modules/@types/node/events.d.ts", 672 | "../../node_modules/@types/node/fs.d.ts", 673 | "../../node_modules/@types/node/fs/promises.d.ts", 674 | "../../node_modules/@types/node/globals.d.ts", 675 | "../../node_modules/@types/node/http.d.ts", 676 | "../../node_modules/@types/node/http2.d.ts", 677 | "../../node_modules/@types/node/https.d.ts", 678 | "../../node_modules/@types/node/inspector.d.ts", 679 | "../../node_modules/@types/node/module.d.ts", 680 | "../../node_modules/@types/node/net.d.ts", 681 | "../../node_modules/@types/node/os.d.ts", 682 | "../../node_modules/@types/node/path.d.ts", 683 | "../../node_modules/@types/node/perf_hooks.d.ts", 684 | "../../node_modules/@types/node/process.d.ts", 685 | "../../node_modules/@types/node/punycode.d.ts", 686 | "../../node_modules/@types/node/querystring.d.ts", 687 | "../../node_modules/@types/node/readline.d.ts", 688 | "../../node_modules/@types/node/repl.d.ts", 689 | "../../node_modules/@types/node/stream.d.ts", 690 | "../../node_modules/@types/node/string_decoder.d.ts", 691 | "../../node_modules/@types/node/timers.d.ts", 692 | "../../node_modules/@types/node/tls.d.ts", 693 | "../../node_modules/@types/node/trace_events.d.ts", 694 | "../../node_modules/@types/node/tty.d.ts", 695 | "../../node_modules/@types/node/url.d.ts", 696 | "../../node_modules/@types/node/util.d.ts", 697 | "../../node_modules/@types/node/v8.d.ts", 698 | "../../node_modules/@types/node/vm.d.ts", 699 | "../../node_modules/@types/node/worker_threads.d.ts", 700 | "../../node_modules/@types/node/zlib.d.ts" 701 | ], 702 | "../../node_modules/@types/node/ts3.6/base.d.ts": [ 703 | "../../node_modules/@types/node/globals.global.d.ts", 704 | "../../node_modules/@types/node/ts3.4/base.d.ts", 705 | "../../node_modules/@types/node/wasi.d.ts" 706 | ], 707 | "../../node_modules/@types/node/tty.d.ts": [ 708 | "../../node_modules/@types/node/net.d.ts" 709 | ], 710 | "../../node_modules/@types/node/url.d.ts": [ 711 | "../../node_modules/@types/node/querystring.d.ts" 712 | ], 713 | "../../node_modules/@types/node/v8.d.ts": [ 714 | "../../node_modules/@types/node/stream.d.ts" 715 | ], 716 | "../../node_modules/@types/node/worker_threads.d.ts": [ 717 | "../../node_modules/@types/node/events.d.ts", 718 | "../../node_modules/@types/node/fs/promises.d.ts", 719 | "../../node_modules/@types/node/stream.d.ts", 720 | "../../node_modules/@types/node/url.d.ts", 721 | "../../node_modules/@types/node/vm.d.ts" 722 | ], 723 | "../../node_modules/@types/node/zlib.d.ts": [ 724 | "../../node_modules/@types/node/stream.d.ts" 725 | ], 726 | "../../node_modules/@types/yargs/index.d.ts": [ 727 | "../../node_modules/@types/yargs-parser/index.d.ts" 728 | ], 729 | "../../node_modules/jest-diff/build/difflines.d.ts": [ 730 | "../../node_modules/jest-diff/build/cleanupsemantic.d.ts", 731 | "../../node_modules/jest-diff/build/types.d.ts" 732 | ], 733 | "../../node_modules/jest-diff/build/index.d.ts": [ 734 | "../../node_modules/jest-diff/build/cleanupsemantic.d.ts", 735 | "../../node_modules/jest-diff/build/difflines.d.ts", 736 | "../../node_modules/jest-diff/build/printdiffs.d.ts", 737 | "../../node_modules/jest-diff/build/types.d.ts" 738 | ], 739 | "../../node_modules/jest-diff/build/printdiffs.d.ts": [ 740 | "../../node_modules/jest-diff/build/cleanupsemantic.d.ts", 741 | "../../node_modules/jest-diff/build/types.d.ts" 742 | ], 743 | "../../node_modules/pretty-format/build/index.d.ts": [ 744 | "../../node_modules/pretty-format/build/types.d.ts" 745 | ] 746 | }, 747 | "exportedModulesMap": { 748 | "../../node_modules/@babel/parser/typings/babel-parser.d.ts": [ 749 | "../../node_modules/@babel/types/lib/index.d.ts" 750 | ], 751 | "../../node_modules/@types/babel__core/index.d.ts": [ 752 | "../../node_modules/@babel/parser/typings/babel-parser.d.ts", 753 | "../../node_modules/@babel/types/lib/index.d.ts", 754 | "../../node_modules/@types/babel__generator/index.d.ts", 755 | "../../node_modules/@types/babel__template/index.d.ts", 756 | "../../node_modules/@types/babel__traverse/index.d.ts" 757 | ], 758 | "../../node_modules/@types/babel__generator/index.d.ts": [ 759 | "../../node_modules/@babel/types/lib/index.d.ts" 760 | ], 761 | "../../node_modules/@types/babel__template/index.d.ts": [ 762 | "../../node_modules/@babel/parser/typings/babel-parser.d.ts", 763 | "../../node_modules/@babel/types/lib/index.d.ts" 764 | ], 765 | "../../node_modules/@types/babel__traverse/index.d.ts": [ 766 | "../../node_modules/@babel/types/lib/index.d.ts" 767 | ], 768 | "../../node_modules/@types/graceful-fs/index.d.ts": [ 769 | "../../node_modules/@types/node/fs.d.ts", 770 | "../../node_modules/@types/node/index.d.ts" 771 | ], 772 | "../../node_modules/@types/istanbul-lib-report/index.d.ts": [ 773 | "../../node_modules/@types/istanbul-lib-coverage/index.d.ts" 774 | ], 775 | "../../node_modules/@types/istanbul-reports/index.d.ts": [ 776 | "../../node_modules/@types/istanbul-lib-report/index.d.ts" 777 | ], 778 | "../../node_modules/@types/jest/index.d.ts": [ 779 | "../../node_modules/jest-diff/build/index.d.ts", 780 | "../../node_modules/pretty-format/build/index.d.ts" 781 | ], 782 | "../../node_modules/@types/node/base.d.ts": [ 783 | "../../node_modules/@types/node/assert.d.ts", 784 | "../../node_modules/@types/node/ts3.6/base.d.ts" 785 | ], 786 | "../../node_modules/@types/node/child_process.d.ts": [ 787 | "../../node_modules/@types/node/events.d.ts", 788 | "../../node_modules/@types/node/fs.d.ts", 789 | "../../node_modules/@types/node/net.d.ts", 790 | "../../node_modules/@types/node/stream.d.ts" 791 | ], 792 | "../../node_modules/@types/node/cluster.d.ts": [ 793 | "../../node_modules/@types/node/child_process.d.ts", 794 | "../../node_modules/@types/node/events.d.ts", 795 | "../../node_modules/@types/node/net.d.ts" 796 | ], 797 | "../../node_modules/@types/node/console.d.ts": [ 798 | "../../node_modules/@types/node/util.d.ts" 799 | ], 800 | "../../node_modules/@types/node/constants.d.ts": [ 801 | "../../node_modules/@types/node/crypto.d.ts", 802 | "../../node_modules/@types/node/fs.d.ts", 803 | "../../node_modules/@types/node/os.d.ts" 804 | ], 805 | "../../node_modules/@types/node/crypto.d.ts": [ 806 | "../../node_modules/@types/node/stream.d.ts" 807 | ], 808 | "../../node_modules/@types/node/dgram.d.ts": [ 809 | "../../node_modules/@types/node/dns.d.ts", 810 | "../../node_modules/@types/node/events.d.ts", 811 | "../../node_modules/@types/node/net.d.ts" 812 | ], 813 | "../../node_modules/@types/node/domain.d.ts": [ 814 | "../../node_modules/@types/node/events.d.ts" 815 | ], 816 | "../../node_modules/@types/node/events.d.ts": [ 817 | "../../node_modules/@types/node/events.d.ts" 818 | ], 819 | "../../node_modules/@types/node/fs.d.ts": [ 820 | "../../node_modules/@types/node/events.d.ts", 821 | "../../node_modules/@types/node/fs/promises.d.ts", 822 | "../../node_modules/@types/node/stream.d.ts", 823 | "../../node_modules/@types/node/url.d.ts" 824 | ], 825 | "../../node_modules/@types/node/fs/promises.d.ts": [ 826 | "../../node_modules/@types/node/fs.d.ts" 827 | ], 828 | "../../node_modules/@types/node/http.d.ts": [ 829 | "../../node_modules/@types/node/net.d.ts", 830 | "../../node_modules/@types/node/stream.d.ts", 831 | "../../node_modules/@types/node/url.d.ts" 832 | ], 833 | "../../node_modules/@types/node/http2.d.ts": [ 834 | "../../node_modules/@types/node/events.d.ts", 835 | "../../node_modules/@types/node/fs.d.ts", 836 | "../../node_modules/@types/node/http.d.ts", 837 | "../../node_modules/@types/node/net.d.ts", 838 | "../../node_modules/@types/node/stream.d.ts", 839 | "../../node_modules/@types/node/tls.d.ts", 840 | "../../node_modules/@types/node/url.d.ts" 841 | ], 842 | "../../node_modules/@types/node/https.d.ts": [ 843 | "../../node_modules/@types/node/events.d.ts", 844 | "../../node_modules/@types/node/http.d.ts", 845 | "../../node_modules/@types/node/tls.d.ts", 846 | "../../node_modules/@types/node/url.d.ts" 847 | ], 848 | "../../node_modules/@types/node/index.d.ts": [ 849 | "../../node_modules/@types/node/base.d.ts" 850 | ], 851 | "../../node_modules/@types/node/inspector.d.ts": [ 852 | "../../node_modules/@types/node/events.d.ts" 853 | ], 854 | "../../node_modules/@types/node/module.d.ts": [ 855 | "../../node_modules/@types/node/url.d.ts" 856 | ], 857 | "../../node_modules/@types/node/net.d.ts": [ 858 | "../../node_modules/@types/node/dns.d.ts", 859 | "../../node_modules/@types/node/events.d.ts", 860 | "../../node_modules/@types/node/stream.d.ts" 861 | ], 862 | "../../node_modules/@types/node/perf_hooks.d.ts": [ 863 | "../../node_modules/@types/node/async_hooks.d.ts" 864 | ], 865 | "../../node_modules/@types/node/process.d.ts": [ 866 | "../../node_modules/@types/node/tty.d.ts" 867 | ], 868 | "../../node_modules/@types/node/readline.d.ts": [ 869 | "../../node_modules/@types/node/events.d.ts", 870 | "../../node_modules/@types/node/stream.d.ts" 871 | ], 872 | "../../node_modules/@types/node/repl.d.ts": [ 873 | "../../node_modules/@types/node/readline.d.ts", 874 | "../../node_modules/@types/node/util.d.ts", 875 | "../../node_modules/@types/node/vm.d.ts" 876 | ], 877 | "../../node_modules/@types/node/stream.d.ts": [ 878 | "../../node_modules/@types/node/events.d.ts" 879 | ], 880 | "../../node_modules/@types/node/tls.d.ts": [ 881 | "../../node_modules/@types/node/crypto.d.ts", 882 | "../../node_modules/@types/node/dns.d.ts", 883 | "../../node_modules/@types/node/net.d.ts", 884 | "../../node_modules/@types/node/stream.d.ts" 885 | ], 886 | "../../node_modules/@types/node/ts3.4/base.d.ts": [ 887 | "../../node_modules/@types/node/async_hooks.d.ts", 888 | "../../node_modules/@types/node/buffer.d.ts", 889 | "../../node_modules/@types/node/child_process.d.ts", 890 | "../../node_modules/@types/node/cluster.d.ts", 891 | "../../node_modules/@types/node/console.d.ts", 892 | "../../node_modules/@types/node/constants.d.ts", 893 | "../../node_modules/@types/node/crypto.d.ts", 894 | "../../node_modules/@types/node/dgram.d.ts", 895 | "../../node_modules/@types/node/dns.d.ts", 896 | "../../node_modules/@types/node/domain.d.ts", 897 | "../../node_modules/@types/node/events.d.ts", 898 | "../../node_modules/@types/node/fs.d.ts", 899 | "../../node_modules/@types/node/fs/promises.d.ts", 900 | "../../node_modules/@types/node/globals.d.ts", 901 | "../../node_modules/@types/node/http.d.ts", 902 | "../../node_modules/@types/node/http2.d.ts", 903 | "../../node_modules/@types/node/https.d.ts", 904 | "../../node_modules/@types/node/inspector.d.ts", 905 | "../../node_modules/@types/node/module.d.ts", 906 | "../../node_modules/@types/node/net.d.ts", 907 | "../../node_modules/@types/node/os.d.ts", 908 | "../../node_modules/@types/node/path.d.ts", 909 | "../../node_modules/@types/node/perf_hooks.d.ts", 910 | "../../node_modules/@types/node/process.d.ts", 911 | "../../node_modules/@types/node/punycode.d.ts", 912 | "../../node_modules/@types/node/querystring.d.ts", 913 | "../../node_modules/@types/node/readline.d.ts", 914 | "../../node_modules/@types/node/repl.d.ts", 915 | "../../node_modules/@types/node/stream.d.ts", 916 | "../../node_modules/@types/node/string_decoder.d.ts", 917 | "../../node_modules/@types/node/timers.d.ts", 918 | "../../node_modules/@types/node/tls.d.ts", 919 | "../../node_modules/@types/node/trace_events.d.ts", 920 | "../../node_modules/@types/node/tty.d.ts", 921 | "../../node_modules/@types/node/url.d.ts", 922 | "../../node_modules/@types/node/util.d.ts", 923 | "../../node_modules/@types/node/v8.d.ts", 924 | "../../node_modules/@types/node/vm.d.ts", 925 | "../../node_modules/@types/node/worker_threads.d.ts", 926 | "../../node_modules/@types/node/zlib.d.ts" 927 | ], 928 | "../../node_modules/@types/node/ts3.6/base.d.ts": [ 929 | "../../node_modules/@types/node/globals.global.d.ts", 930 | "../../node_modules/@types/node/ts3.4/base.d.ts", 931 | "../../node_modules/@types/node/wasi.d.ts" 932 | ], 933 | "../../node_modules/@types/node/tty.d.ts": [ 934 | "../../node_modules/@types/node/net.d.ts" 935 | ], 936 | "../../node_modules/@types/node/url.d.ts": [ 937 | "../../node_modules/@types/node/querystring.d.ts" 938 | ], 939 | "../../node_modules/@types/node/v8.d.ts": [ 940 | "../../node_modules/@types/node/stream.d.ts" 941 | ], 942 | "../../node_modules/@types/node/worker_threads.d.ts": [ 943 | "../../node_modules/@types/node/events.d.ts", 944 | "../../node_modules/@types/node/fs/promises.d.ts", 945 | "../../node_modules/@types/node/stream.d.ts", 946 | "../../node_modules/@types/node/url.d.ts", 947 | "../../node_modules/@types/node/vm.d.ts" 948 | ], 949 | "../../node_modules/@types/node/zlib.d.ts": [ 950 | "../../node_modules/@types/node/stream.d.ts" 951 | ], 952 | "../../node_modules/@types/yargs/index.d.ts": [ 953 | "../../node_modules/@types/yargs-parser/index.d.ts" 954 | ], 955 | "../../node_modules/jest-diff/build/difflines.d.ts": [ 956 | "../../node_modules/jest-diff/build/cleanupsemantic.d.ts", 957 | "../../node_modules/jest-diff/build/types.d.ts" 958 | ], 959 | "../../node_modules/jest-diff/build/index.d.ts": [ 960 | "../../node_modules/jest-diff/build/cleanupsemantic.d.ts", 961 | "../../node_modules/jest-diff/build/difflines.d.ts", 962 | "../../node_modules/jest-diff/build/printdiffs.d.ts", 963 | "../../node_modules/jest-diff/build/types.d.ts" 964 | ], 965 | "../../node_modules/jest-diff/build/printdiffs.d.ts": [ 966 | "../../node_modules/jest-diff/build/cleanupsemantic.d.ts", 967 | "../../node_modules/jest-diff/build/types.d.ts" 968 | ], 969 | "../../node_modules/pretty-format/build/index.d.ts": [ 970 | "../../node_modules/pretty-format/build/types.d.ts" 971 | ] 972 | }, 973 | "semanticDiagnosticsPerFile": [ 974 | "./index.ts", 975 | "../../node_modules/@babel/parser/typings/babel-parser.d.ts", 976 | "../../node_modules/@babel/types/lib/index.d.ts", 977 | "../../node_modules/@types/babel__core/index.d.ts", 978 | "../../node_modules/@types/babel__generator/index.d.ts", 979 | "../../node_modules/@types/babel__template/index.d.ts", 980 | "../../node_modules/@types/babel__traverse/index.d.ts", 981 | "../../node_modules/@types/graceful-fs/index.d.ts", 982 | "../../node_modules/@types/istanbul-lib-coverage/index.d.ts", 983 | "../../node_modules/@types/istanbul-lib-report/index.d.ts", 984 | "../../node_modules/@types/istanbul-reports/index.d.ts", 985 | "../../node_modules/@types/jest/index.d.ts", 986 | "../../node_modules/@types/node/assert.d.ts", 987 | "../../node_modules/@types/node/async_hooks.d.ts", 988 | "../../node_modules/@types/node/base.d.ts", 989 | "../../node_modules/@types/node/buffer.d.ts", 990 | "../../node_modules/@types/node/child_process.d.ts", 991 | "../../node_modules/@types/node/cluster.d.ts", 992 | "../../node_modules/@types/node/console.d.ts", 993 | "../../node_modules/@types/node/constants.d.ts", 994 | "../../node_modules/@types/node/crypto.d.ts", 995 | "../../node_modules/@types/node/dgram.d.ts", 996 | "../../node_modules/@types/node/dns.d.ts", 997 | "../../node_modules/@types/node/domain.d.ts", 998 | "../../node_modules/@types/node/events.d.ts", 999 | "../../node_modules/@types/node/fs.d.ts", 1000 | "../../node_modules/@types/node/fs/promises.d.ts", 1001 | "../../node_modules/@types/node/globals.d.ts", 1002 | "../../node_modules/@types/node/globals.global.d.ts", 1003 | "../../node_modules/@types/node/http.d.ts", 1004 | "../../node_modules/@types/node/http2.d.ts", 1005 | "../../node_modules/@types/node/https.d.ts", 1006 | "../../node_modules/@types/node/index.d.ts", 1007 | "../../node_modules/@types/node/inspector.d.ts", 1008 | "../../node_modules/@types/node/module.d.ts", 1009 | "../../node_modules/@types/node/net.d.ts", 1010 | "../../node_modules/@types/node/os.d.ts", 1011 | "../../node_modules/@types/node/path.d.ts", 1012 | "../../node_modules/@types/node/perf_hooks.d.ts", 1013 | "../../node_modules/@types/node/process.d.ts", 1014 | "../../node_modules/@types/node/punycode.d.ts", 1015 | "../../node_modules/@types/node/querystring.d.ts", 1016 | "../../node_modules/@types/node/readline.d.ts", 1017 | "../../node_modules/@types/node/repl.d.ts", 1018 | "../../node_modules/@types/node/stream.d.ts", 1019 | "../../node_modules/@types/node/string_decoder.d.ts", 1020 | "../../node_modules/@types/node/timers.d.ts", 1021 | "../../node_modules/@types/node/tls.d.ts", 1022 | "../../node_modules/@types/node/trace_events.d.ts", 1023 | "../../node_modules/@types/node/ts3.4/base.d.ts", 1024 | "../../node_modules/@types/node/ts3.6/base.d.ts", 1025 | "../../node_modules/@types/node/tty.d.ts", 1026 | "../../node_modules/@types/node/url.d.ts", 1027 | "../../node_modules/@types/node/util.d.ts", 1028 | "../../node_modules/@types/node/v8.d.ts", 1029 | "../../node_modules/@types/node/vm.d.ts", 1030 | "../../node_modules/@types/node/wasi.d.ts", 1031 | "../../node_modules/@types/node/worker_threads.d.ts", 1032 | "../../node_modules/@types/node/zlib.d.ts", 1033 | "../../node_modules/@types/normalize-package-data/index.d.ts", 1034 | "../../node_modules/@types/prettier/index.d.ts", 1035 | "../../node_modules/@types/stack-utils/index.d.ts", 1036 | "../../node_modules/@types/yargs-parser/index.d.ts", 1037 | "../../node_modules/@types/yargs/index.d.ts", 1038 | "../../node_modules/jest-diff/build/cleanupsemantic.d.ts", 1039 | "../../node_modules/jest-diff/build/difflines.d.ts", 1040 | "../../node_modules/jest-diff/build/index.d.ts", 1041 | "../../node_modules/jest-diff/build/printdiffs.d.ts", 1042 | "../../node_modules/jest-diff/build/types.d.ts", 1043 | "../../node_modules/pretty-format/build/index.d.ts", 1044 | "../../node_modules/pretty-format/build/types.d.ts", 1045 | "../../node_modules/typescript/lib/lib.dom.d.ts", 1046 | "../../node_modules/typescript/lib/lib.es2015.collection.d.ts", 1047 | "../../node_modules/typescript/lib/lib.es2015.core.d.ts", 1048 | "../../node_modules/typescript/lib/lib.es2015.d.ts", 1049 | "../../node_modules/typescript/lib/lib.es2015.generator.d.ts", 1050 | "../../node_modules/typescript/lib/lib.es2015.iterable.d.ts", 1051 | "../../node_modules/typescript/lib/lib.es2015.promise.d.ts", 1052 | "../../node_modules/typescript/lib/lib.es2015.proxy.d.ts", 1053 | "../../node_modules/typescript/lib/lib.es2015.reflect.d.ts", 1054 | "../../node_modules/typescript/lib/lib.es2015.symbol.d.ts", 1055 | "../../node_modules/typescript/lib/lib.es2015.symbol.wellknown.d.ts", 1056 | "../../node_modules/typescript/lib/lib.es2016.array.include.d.ts", 1057 | "../../node_modules/typescript/lib/lib.es2016.d.ts", 1058 | "../../node_modules/typescript/lib/lib.es2017.d.ts", 1059 | "../../node_modules/typescript/lib/lib.es2017.intl.d.ts", 1060 | "../../node_modules/typescript/lib/lib.es2017.object.d.ts", 1061 | "../../node_modules/typescript/lib/lib.es2017.sharedmemory.d.ts", 1062 | "../../node_modules/typescript/lib/lib.es2017.string.d.ts", 1063 | "../../node_modules/typescript/lib/lib.es2017.typedarrays.d.ts", 1064 | "../../node_modules/typescript/lib/lib.es2018.asyncgenerator.d.ts", 1065 | "../../node_modules/typescript/lib/lib.es2018.asynciterable.d.ts", 1066 | "../../node_modules/typescript/lib/lib.es2018.d.ts", 1067 | "../../node_modules/typescript/lib/lib.es2018.intl.d.ts", 1068 | "../../node_modules/typescript/lib/lib.es2018.promise.d.ts", 1069 | "../../node_modules/typescript/lib/lib.es2018.regexp.d.ts", 1070 | "../../node_modules/typescript/lib/lib.es2020.bigint.d.ts", 1071 | "../../node_modules/typescript/lib/lib.es5.d.ts", 1072 | "../../node_modules/typescript/lib/lib.esnext.intl.d.ts" 1073 | ] 1074 | }, 1075 | "version": "4.1.3" 1076 | } -------------------------------------------------------------------------------- /chapters/chapter-2_Core_Principles_and_use_cases/advanced.ts: -------------------------------------------------------------------------------- 1 | export type PartialRecord = { 2 | [P in K]?: T; 3 | }; 4 | 5 | interface SignupFormState { 6 | email: string; 7 | name: string; 8 | } 9 | 10 | interface ActionPayload { 11 | key: keyof SignupFormState; 12 | value: string; 13 | } 14 | 15 | const update1: ActionPayload = { 16 | key: "email", 17 | value: "hello@gmail.com", 18 | }; 19 | 20 | type actionPayloadKeys = keyof typeof update1; 21 | 22 | type Point2d = { x: number; y: number }; 23 | 24 | type NominalTyped = Type & { __type: Brand }; 25 | 26 | type Point3d = NominalTyped; 27 | 28 | function distance1(first: Point2d, second: Point2d) { 29 | return Math.sqrt( 30 | Math.pow(first.x - second.x, 2) + Math.pow(first.y - second.y, 2) 31 | ); 32 | } 33 | distance1({ x: 1, y: 2 }, { x: 3, y: 4 }); 34 | 35 | function distance2( 36 | first: NominalTyped, 37 | second: NominalTyped 38 | ) { 39 | return Math.sqrt( 40 | Math.pow(first.x - second.x, 2) + Math.pow(first.y - second.y, 2) 41 | ); 42 | } 43 | 44 | class User { 45 | private static readonly __type: unique symbol = Symbol(); 46 | name: string; 47 | constructor(name: string) { 48 | this.name = name; 49 | } 50 | } 51 | type Account = { 52 | name: string; 53 | }; 54 | function printAccountName(o: User) { 55 | console.log(o.name); 56 | } 57 | printAccountName(new User("Theo")); 58 | // printAccountName({name: "Alex"}) // Fail to typecheck 59 | 60 | type PPSNumber = { 61 | number: string; 62 | }; 63 | type NameOrPPSNumber = T extends number 64 | ? PPSNumber 65 | : string; 66 | 67 | const loginInfo: NameOrPPSNumber<1> = { 68 | number: "123", 69 | }; 70 | 71 | interface Box { 72 | value: T; 73 | } 74 | 75 | type UnpackBox = A extends Box ? E : A; 76 | 77 | type intStash = UnpackBox<{ value: 10 }>; 78 | type stringStash = UnpackBox<{ value: "123" }>; 79 | type booleanStash = UnpackBox; 80 | -------------------------------------------------------------------------------- /chapters/chapter-2_Core_Principles_and_use_cases/deno/index.ts: -------------------------------------------------------------------------------- 1 | // @ts-ignore 2 | import { listenAndServe } from "https://deno.land/std@0.88.0/http/server.ts"; 3 | 4 | const serverConfig = { 5 | port: 8000, 6 | }; 7 | 8 | listenAndServe(serverConfig, (req) => { 9 | req.respond({ body: "Hello World\n" }); 10 | }); 11 | 12 | console.info(`Server started at http://localhost:8000`); 13 | -------------------------------------------------------------------------------- /chapters/chapter-2_Core_Principles_and_use_cases/express/app.ts: -------------------------------------------------------------------------------- 1 | import express from "express"; 2 | import bodyParser from "body-parser"; 3 | import morgan from "morgan"; 4 | 5 | const app = express(); 6 | const logger = morgan("combined"); 7 | 8 | app.use( 9 | bodyParser.urlencoded({ 10 | extended: true, 11 | }) 12 | ); 13 | app.use(bodyParser.json()); 14 | app.use(logger); 15 | 16 | export default app; 17 | -------------------------------------------------------------------------------- /chapters/chapter-2_Core_Principles_and_use_cases/express/container.ts: -------------------------------------------------------------------------------- 1 | import { Container } from "inversify"; 2 | 3 | import "./health.controller"; 4 | 5 | // set up container 6 | const container = new Container(); 7 | 8 | // set up bindings 9 | 10 | export default container; 11 | -------------------------------------------------------------------------------- /chapters/chapter-2_Core_Principles_and_use_cases/express/health.controller.ts: -------------------------------------------------------------------------------- 1 | import { injectable, inject } from "inversify"; 2 | import { 3 | controller, 4 | httpGet, 5 | BaseHttpController, 6 | } from "inversify-express-utils"; 7 | 8 | @controller("/health") 9 | export class HealthController extends BaseHttpController { 10 | @httpGet("/") 11 | public async get() { 12 | return this.ok("All good!"); 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /chapters/chapter-2_Core_Principles_and_use_cases/express/server.ts: -------------------------------------------------------------------------------- 1 | import "reflect-metadata"; 2 | import { interfaces, InversifyExpressServer } from "inversify-express-utils"; 3 | 4 | import app from "./app"; 5 | import container from "./container"; 6 | 7 | const port = process.env.PORT || 3000; 8 | 9 | let server = new InversifyExpressServer(container, null, null, app); 10 | server.build().listen(port); 11 | -------------------------------------------------------------------------------- /chapters/chapter-2_Core_Principles_and_use_cases/node-error/index.ts: -------------------------------------------------------------------------------- 1 | export class AppError extends Error { 2 | public readonly isOperational: boolean; 3 | 4 | constructor(description: string, isOperational: boolean) { 5 | super(description); 6 | this.name = this.constructor.name; 7 | this.isOperational = isOperational; 8 | Error.captureStackTrace(this); 9 | } 10 | } 11 | 12 | function isAppError(error: Error | AppError): error is AppError { 13 | return (error as AppError).isOperational !== undefined; 14 | } 15 | 16 | class ErrorManagementService { 17 | public handleError(err: Error): void { 18 | console.log("Handling error:", err.message); 19 | } 20 | 21 | public isTrusted(error: Error) { 22 | if (isAppError(error)) { 23 | return error.isOperational; 24 | } 25 | return false; 26 | } 27 | } 28 | 29 | const errorManagementService = new ErrorManagementService(); 30 | 31 | process.on("uncaughtException", (error: Error) => { 32 | errorManagementService.handleError(error); 33 | if (!errorManagementService.isTrusted(error)) { 34 | console.log("Exiting because of error"); 35 | process.exit(1); 36 | } 37 | }); 38 | 39 | throw new AppError("Invalid use case", true); // Untrusted - Exit 40 | // throw new Error("Invalid use case"); // Untrusted - Exit 41 | // throw new AppError("Invalid use case", true); // Trusted - continue 42 | -------------------------------------------------------------------------------- /chapters/chapter-2_Core_Principles_and_use_cases/oop.ts: -------------------------------------------------------------------------------- 1 | interface RestApiClient { 2 | getAll(): Promise; 3 | getOne(id: string): Promise; 4 | } 5 | 6 | interface Site { 7 | name: string; 8 | } 9 | 10 | class SitesApiClient implements RestApiClient { 11 | getAll() { 12 | const res: Site[] = [{ name: "website1" }]; 13 | return Promise.resolve(res); 14 | } 15 | getOne(id: string) { 16 | return Promise.resolve({ name: "website1" }); 17 | } 18 | } 19 | 20 | class EventAction { 21 | trigger(delay: number = 0) { 22 | console.log(`Event triggered in ${delay}s.`); 23 | } 24 | } 25 | class NotificationEvent extends EventAction { 26 | sendEmail() { 27 | console.log("Sending Email"); 28 | } 29 | } 30 | const ev = new NotificationEvent(); 31 | ev.trigger(); 32 | ev.sendEmail(); 33 | ev.trigger(10); 34 | 35 | class A { 36 | constructor() { 37 | this.subClassCheck(); 38 | } 39 | 40 | private subClassCheck(): never | void { 41 | if (Object.getPrototypeOf(this) !== A.prototype) { 42 | throw new Error("Cannot extend this class."); 43 | } 44 | } 45 | } 46 | 47 | class B extends A {} 48 | 49 | let a = new A(); // OK 50 | // let b = new B(); // fail 51 | 52 | class User { 53 | #name: string; 54 | 55 | constructor(name: string) { 56 | this.#name = name; 57 | } 58 | 59 | greet() { 60 | console.log(`User: ${this.#name}!`); 61 | } 62 | } 63 | 64 | const theo = new User("Theo"); 65 | // theo.#name; // cannot access private field 66 | 67 | class Component { 68 | onInit(): void { 69 | console.log("Called from Component"); 70 | } 71 | } 72 | 73 | class ReactComponent extends Component { 74 | onInit(): void { 75 | super.onInit(); 76 | console.log("Called from React Component"); 77 | } 78 | } 79 | 80 | const c = new ReactComponent(); 81 | c.onInit(); // logs "Called from React Component" 82 | -------------------------------------------------------------------------------- /chapters/chapter-2_Core_Principles_and_use_cases/partial.ts: -------------------------------------------------------------------------------- 1 | enum PRIORITY { 2 | DEFAULT, 3 | 4 | LOW, 5 | 6 | HIGH, 7 | } 8 | 9 | interface TodoItemProps { 10 | title: string; 11 | 12 | description: string; 13 | 14 | priority: PRIORITY; 15 | } 16 | 17 | class TodoItem { 18 | description?: string; 19 | 20 | title = "Default item title"; 21 | 22 | priority = PRIORITY.DEFAULT; 23 | 24 | constructor(todoItemProps: Partial = {}) { 25 | Object.assign(this, todoItemProps); 26 | } 27 | } 28 | 29 | const item = new TodoItem({ description: "Some description" }); 30 | 31 | console.debug(item.description); // prints "Some description" 32 | 33 | console.debug(item.title); // prints "Some description" 34 | -------------------------------------------------------------------------------- /chapters/chapter-2_Core_Principles_and_use_cases/react/app.tsx: -------------------------------------------------------------------------------- 1 | import * as React from "react"; 2 | import * as ReactDOM from "react-dom"; 3 | 4 | interface AppProps { 5 | greeting: string; 6 | } 7 | 8 | const App: React.FC = ({ greeting = "Hello" }) => { 9 | return ( 10 |
11 |

{greeting}

12 |
13 | ); 14 | }; 15 | 16 | ReactDOM.render( 17 | , 18 | document.getElementById("app") 19 | ); 20 | -------------------------------------------------------------------------------- /chapters/chapter-2_Core_Principles_and_use_cases/react/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Document 5 | 6 | 7 | 8 |
9 | 10 | 11 | -------------------------------------------------------------------------------- /chapters/chapter-2_Core_Principles_and_use_cases/react/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "target": "es5", 4 | "moduleResolution": "node", 5 | "sourceMap": true, 6 | "jsx": "react", 7 | "strict": true, 8 | }, 9 | "include": [ 10 | "**/*.ts", 11 | "**/*.tsx" 12 | ], 13 | "exclude": [ 14 | "node_modules" 15 | ] 16 | } -------------------------------------------------------------------------------- /chapters/chapter-2_Core_Principles_and_use_cases/react/webpack.config.js: -------------------------------------------------------------------------------- 1 | const path = require("path"); 2 | 3 | module.exports = { 4 | entry: path.resolve(__dirname, "./app.tsx"), 5 | devtool: "inline-source-map", 6 | module: { 7 | rules: [ 8 | { 9 | test: /\.tsx?$/, 10 | use: "ts-loader", 11 | exclude: /node_modules/, 12 | }, 13 | ], 14 | }, 15 | resolve: { 16 | extensions: [".tsx", ".ts", ".js"], 17 | }, 18 | output: { 19 | filename: "index.js", 20 | path: path.resolve(__dirname, "./"), 21 | }, 22 | devServer: { 23 | contentBase: path.join(__dirname, "./"), 24 | compress: true, 25 | port: 9000, 26 | }, 27 | }; 28 | -------------------------------------------------------------------------------- /chapters/chapter-2_Core_Principles_and_use_cases/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "../tsconfig.json", 3 | "compilerOptions": { 4 | // compiler options 5 | "composite": false, 6 | "allowJs": true, 7 | "noImplicitAny": false, 8 | "target": "ES2015" 9 | }, 10 | } -------------------------------------------------------------------------------- /chapters/chapter-2_Core_Principles_and_use_cases/utility.ts: -------------------------------------------------------------------------------- 1 | import { HTMLAttributes } from "react"; 2 | 3 | const serviceConfig: Record = { 4 | port: 3000, 5 | basePath: "http://localhost", 6 | enableStripePayments: false, 7 | }; 8 | 9 | console.log(serviceConfig.port); // prints 3000 10 | 11 | serviceConfig.enablePayments; 12 | 13 | type ServiceConfigParams = "port" | "basePath" | "enableStripePayments"; 14 | const serviceConfigChecked: Record< 15 | ServiceConfigParams, 16 | string | number | boolean 17 | > = { 18 | port: 3000, 19 | basePath: "http://localhost", 20 | enableStripePayments: false, 21 | }; 22 | 23 | console.log(serviceConfig.basePath); // prints http://localhost 24 | 25 | enum PRIORITY { 26 | DEFAULT, 27 | LOW, 28 | HIGH, 29 | } 30 | interface TodoItemProps { 31 | title: string; 32 | description: string; 33 | priority: PRIORITY; 34 | } 35 | class TodoItem { 36 | description?: string; 37 | title = "Default item title"; 38 | priority = PRIORITY.DEFAULT; 39 | constructor(todoItemProps: Partial = {}) { 40 | Object.assign(this, todoItemProps); 41 | } 42 | } 43 | 44 | const item = new TodoItem({ description: "Some description" }); 45 | console.debug(item.description); // prints "Some description" 46 | console.debug(item.title); // prints "Some description" 47 | 48 | type OriginalTodoItemProps = Required>; // type is same as TodoItemProps 49 | 50 | type ButtonProps = Pick< 51 | HTMLAttributes, 52 | "onClick" | "onSubmit" | "className" | "onFocus" 53 | >; 54 | 55 | type OriginalThemProps = { 56 | colors: string[]; 57 | elevations: string[]; 58 | margins: string[]; 59 | defaultTypography: string; 60 | }; 61 | type CustomThemeProps = { 62 | colors: Set; 63 | }; 64 | type ThemeProps = Omit & { 65 | colors?: CustomThemeProps; 66 | }; 67 | -------------------------------------------------------------------------------- /chapters/chapter-2_Core_Principles_and_use_cases/webpack/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Document 5 | 6 | 7 | 8 |
Typescript 4 Design Patterns
9 |

10 | 11 | 12 | 13 | 14 | -------------------------------------------------------------------------------- /chapters/chapter-2_Core_Principles_and_use_cases/webpack/index.js: -------------------------------------------------------------------------------- 1 | /******/ (() => { // webpackBootstrap 2 | /******/ "use strict"; 3 | var __webpack_exports__ = {}; 4 | /******/ })() 5 | ; -------------------------------------------------------------------------------- /chapters/chapter-2_Core_Principles_and_use_cases/webpack/index.ts: -------------------------------------------------------------------------------- 1 | const par = document.getElementsByClassName("paragraph")[0]; 2 | 3 | const span = document.createElement("span"); 4 | span.textContent = "This is a text we added dynamically"; 5 | par?.appendChild(span); 6 | 7 | const button = document.querySelector("button"); 8 | button?.addEventListener("click", () => { 9 | window.alert("Did you Clicked the Submit Button"); 10 | }); 11 | -------------------------------------------------------------------------------- /chapters/chapter-2_Core_Principles_and_use_cases/webpack/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "module": "commonjs", 4 | "target": "es5", 5 | "esModuleInterop": true 6 | } 7 | } 8 | -------------------------------------------------------------------------------- /chapters/chapter-2_Core_Principles_and_use_cases/webpack/webpack.config.js: -------------------------------------------------------------------------------- 1 | const path = require("path"); 2 | 3 | module.exports = { 4 | entry: path.resolve(__dirname, "./index.ts"), 5 | devtool: "inline-source-map", 6 | module: { 7 | rules: [ 8 | { 9 | test: /\.tsx?$/, 10 | use: "ts-loader", 11 | exclude: /node_modules/, 12 | }, 13 | ], 14 | }, 15 | resolve: { 16 | extensions: [".tsx", ".ts", ".js"], 17 | }, 18 | output: { 19 | filename: "index.js", 20 | path: path.resolve(__dirname, "./"), 21 | }, 22 | devServer: { 23 | contentBase: path.join(__dirname, "./"), 24 | compress: true, 25 | port: 9000, 26 | }, 27 | }; 28 | -------------------------------------------------------------------------------- /chapters/chapter-3_Creational_Design_Patterns/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PacktPublishing/TypeScript-4-Design-Patterns-and-Best-Practices/794888bd4c351b9565e01828440e3d0cf06d26c6/chapters/chapter-3_Creational_Design_Patterns/.gitkeep -------------------------------------------------------------------------------- /chapters/chapter-3_Creational_Design_Patterns/AbstractFactory.test.ts: -------------------------------------------------------------------------------- 1 | import { 2 | HTMLWebsitePageFactory, 3 | WebsitePageFactory, 4 | HTMLContent, 5 | HTMLHeader, 6 | HTMLFooter, 7 | } from "./AbstractFactory"; 8 | 9 | const wpf: WebsitePageFactory = new HTMLWebsitePageFactory(); 10 | test("it creates an HTML Content type", () => { 11 | const content = wpf.createContent("Content"); 12 | expect(content).toBeInstanceOf(HTMLContent); 13 | expect(content.content).toBe("
Content
"); 14 | }); 15 | test("it creates an HTML Header type", () => { 16 | const header = wpf.createHeader("Header"); 17 | expect(header).toBeInstanceOf(HTMLHeader); 18 | expect(header.content).toBe("Header"); 19 | }); 20 | test("it creates an HTML Footer type", () => { 21 | const footer = wpf.createFooter("Footer"); 22 | expect(footer).toBeInstanceOf(HTMLFooter); 23 | expect(footer.content).toBe("
Footer
"); 24 | }); 25 | -------------------------------------------------------------------------------- /chapters/chapter-3_Creational_Design_Patterns/AbstractFactory.ts: -------------------------------------------------------------------------------- 1 | export interface WebsitePageFactory { 2 | createHeader(text: string): Header; 3 | createContent(text: string): Content; 4 | createFooter(text: string): Footer; 5 | } 6 | 7 | interface Header { 8 | content: string; 9 | } 10 | 11 | interface Content { 12 | content: string; 13 | } 14 | 15 | interface Footer { 16 | content: string; 17 | } 18 | 19 | export class HTMLWebsitePageFactory implements WebsitePageFactory { 20 | createHeader(text: string): HTMLHeader { 21 | return new HTMLHeader(text); 22 | } 23 | createContent(text: string): HTMLContent { 24 | return new HTMLContent(text); 25 | } 26 | createFooter(text: string): HTMLFooter { 27 | return new HTMLFooter(text); 28 | } 29 | } 30 | 31 | export class HTMLHeader implements Header { 32 | content: string; 33 | constructor(text: string) { 34 | this.content = `${text}`; 35 | } 36 | } 37 | 38 | export class HTMLContent implements Content { 39 | content: string; 40 | constructor(text: string) { 41 | this.content = `
${text}
`; 42 | } 43 | } 44 | 45 | export class HTMLFooter implements Footer { 46 | content: string; 47 | constructor(text: string) { 48 | this.content = `
${text}
`; 49 | } 50 | } 51 | 52 | const wpf: WebsitePageFactory = new HTMLWebsitePageFactory(); 53 | console.log(wpf.createContent("Content").content); 54 | console.log(wpf.createHeader("Header").content); 55 | console.log(wpf.createFooter("Footer").content); 56 | 57 | type Circle = string; 58 | type CircleProps = { 59 | cx: number; 60 | cy: number; 61 | radius: number; 62 | }; 63 | type RectangleProps = { 64 | x1: number; 65 | y1: number; 66 | width: number; 67 | height: number; 68 | }; 69 | 70 | interface BrowserGraphicsFactory { 71 | drawCircle(props: CircleProps): Circle; 72 | drawRectangle(props: RectangleProps): Circle; 73 | } 74 | 75 | // class CanvasGraphicsFactory implements BrowserGraphicsFactory { 76 | 77 | // } 78 | 79 | // class SVGGraphicsFactory implements BrowserGraphicsFactory { 80 | 81 | // } 82 | -------------------------------------------------------------------------------- /chapters/chapter-3_Creational_Design_Patterns/FactoryMethod.test.ts: -------------------------------------------------------------------------------- 1 | import { 2 | Weapon, 3 | LongBow, 4 | LongSword, 5 | LongBowFactory, 6 | LongSwordFactory, 7 | } from "./FactoryMethod"; 8 | 9 | let lbf: LongBowFactory; 10 | let lsf: LongSwordFactory; 11 | beforeEach(() => { 12 | lbf = new LongBowFactory(); 13 | lsf = new LongSwordFactory(); 14 | }); 15 | 16 | test("it creates a LongBow type using the factory", () => { 17 | const weapon = lbf.create(); 18 | expect(weapon).toBeInstanceOf(LongBow); 19 | }); 20 | 21 | test("it creates a LongSword type using the factory", () => { 22 | const weapon = lsf.create(); 23 | expect(weapon).toBeInstanceOf(LongSword); 24 | }); 25 | -------------------------------------------------------------------------------- /chapters/chapter-3_Creational_Design_Patterns/FactoryMethod.ts: -------------------------------------------------------------------------------- 1 | export interface Weapon { 2 | getName(): string; 3 | getDamage(): number; 4 | getRange(): number; 5 | } 6 | 7 | export class LongSword implements Weapon { 8 | getName(): string { 9 | return "LongSword"; 10 | } 11 | getDamage(): number { 12 | return 10; 13 | } 14 | getRange(): number { 15 | return 2; 16 | } 17 | } 18 | 19 | export class LongBow implements Weapon { 20 | getName(): string { 21 | return "LongBow"; 22 | } 23 | getDamage(): number { 24 | return 8; 25 | } 26 | getRange(): number { 27 | return 16; 28 | } 29 | } 30 | 31 | interface WeaponFactory { 32 | create(): Weapon; 33 | } 34 | 35 | export class LongSwordFactory implements WeaponFactory { 36 | create(): Weapon { 37 | return new LongSword(); 38 | } 39 | } 40 | export class LongBowFactory implements WeaponFactory { 41 | create(): Weapon { 42 | return new LongBow(); 43 | } 44 | } 45 | 46 | const lbf = new LongBowFactory(); 47 | const lsf = new LongSwordFactory(); 48 | const factories: WeaponFactory[] = [lbf, lsf, lbf]; 49 | factories.forEach((f: WeaponFactory) => { 50 | console.debug(f.create()); 51 | }); 52 | 53 | const enum WeaponType { 54 | LONGBOW, 55 | LONGSWORD, 56 | } 57 | 58 | class WeaponCreator { 59 | create(weaponType: WeaponType): Weapon { 60 | switch (weaponType) { 61 | case WeaponType.LONGBOW: { 62 | return new LongBow(); 63 | break; 64 | } 65 | case WeaponType.LONGSWORD: { 66 | return new LongSword(); 67 | break; 68 | } 69 | } 70 | } 71 | } 72 | -------------------------------------------------------------------------------- /chapters/chapter-3_Creational_Design_Patterns/FactoryMethodType.ts: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PacktPublishing/TypeScript-4-Design-Patterns-and-Best-Practices/794888bd4c351b9565e01828440e3d0cf06d26c6/chapters/chapter-3_Creational_Design_Patterns/FactoryMethodType.ts -------------------------------------------------------------------------------- /chapters/chapter-3_Creational_Design_Patterns/InversifySingleton.ts: -------------------------------------------------------------------------------- 1 | import "reflect-metadata"; 2 | import { injectable, Container } from "inversify"; 3 | 4 | interface UsersApiService { 5 | getUsers(): Promise; 6 | } 7 | 8 | let TYPES = { 9 | UsersApiService: Symbol("UsersApiService"), 10 | }; 11 | 12 | @injectable() 13 | class UsersApiServiceImpl implements UsersApiService { 14 | getUsers(): Promise { 15 | return Promise.resolve(["Alex", "John", "Sarah"]); 16 | } 17 | } 18 | 19 | // set up container 20 | const container = new Container(); 21 | // set up bindings 22 | container 23 | .bind(TYPES.UsersApiService) 24 | .to(UsersApiServiceImpl) 25 | .inSingletonScope(); 26 | 27 | container 28 | .get(TYPES.UsersApiService) 29 | .getUsers() 30 | .then((res) => console.log(res)); 31 | export default container; 32 | -------------------------------------------------------------------------------- /chapters/chapter-3_Creational_Design_Patterns/ModuleSingleton.ts: -------------------------------------------------------------------------------- 1 | class ApiServiceSingleton {} 2 | 3 | export default new ApiServiceSingleton(); 4 | -------------------------------------------------------------------------------- /chapters/chapter-3_Creational_Design_Patterns/ParametricSingleton.ts: -------------------------------------------------------------------------------- 1 | export class ParametricSingleton { 2 | private param: string; 3 | // Stores the singletons instances 4 | private static instances: Map; 5 | // Prevents creation of new instances 6 | private constructor(param: string) { 7 | this.param = param; 8 | } 9 | // Method to retrieve instance 10 | static getInstance(param: string) { 11 | if (!ParametricSingleton.instances.has(param)) { 12 | ParametricSingleton.instances.set(param, new ParametricSingleton(param)); 13 | } 14 | return ParametricSingleton.instances.get(param); 15 | } 16 | } 17 | 18 | export default ParametricSingleton; 19 | -------------------------------------------------------------------------------- /chapters/chapter-3_Creational_Design_Patterns/Prototype.test.ts: -------------------------------------------------------------------------------- 1 | import { Warrior, Wizard, HeroPrototype } from "./Prototype"; 2 | 3 | test("it creates a Wizard from a prototype", () => { 4 | const wiz = new Wizard("Theo"); 5 | expect(wiz.clone()).toBeInstanceOf(Wizard); 6 | expect(JSON.stringify(wiz.clone())).toBe( 7 | '{"name":"Unknown","spells":[],"health":100}' 8 | ); 9 | }); 10 | test("it creates a Warrior from a prototype", () => { 11 | const war = new Warrior("Alex"); 12 | expect(war.clone()).toBeInstanceOf(Warrior); 13 | expect(JSON.stringify(war.clone())).toBe( 14 | '{"name":"Alex","weapon":"Bare Hands","health":150}' 15 | ); 16 | }); 17 | -------------------------------------------------------------------------------- /chapters/chapter-3_Creational_Design_Patterns/Prototype.ts: -------------------------------------------------------------------------------- 1 | export interface HeroPrototype { 2 | clone(): HeroPrototype; 3 | } 4 | 5 | export interface Spell { 6 | name: string; 7 | } 8 | 9 | export class Wizard implements HeroPrototype { 10 | private spells: Spell[]; 11 | private health: number; 12 | constructor(private name: string) { 13 | this.spells = []; 14 | this.health = 100; 15 | } 16 | 17 | clone(): Wizard { 18 | const cloned = Object.create(Wizard.prototype || null); 19 | Object.getOwnPropertyNames(this).map((key: string) => { 20 | if (key === "name") { 21 | cloned[key] = "Unknown"; 22 | } else { 23 | cloned[key] = this[key]; 24 | } 25 | }); 26 | 27 | return cloned; 28 | } 29 | } 30 | 31 | export class Warrior implements HeroPrototype { 32 | private weapon: string; 33 | private health: number; 34 | constructor(private name: string) { 35 | this.weapon = "Dagger"; 36 | this.health = 150; 37 | } 38 | 39 | clone(): Warrior { 40 | const cloned = Object.create(Warrior.prototype || null); 41 | Object.getOwnPropertyNames(this).map((key: string) => { 42 | if (key === "weapon") { 43 | cloned[key] = "Bare Hands"; 44 | } else { 45 | cloned[key] = this[key]; 46 | } 47 | }); 48 | 49 | return cloned; 50 | } 51 | } 52 | 53 | let wiz: HeroPrototype = new Wizard("Theo"); 54 | let war: HeroPrototype = new Warrior("Mike"); 55 | console.debug(wiz.clone()); 56 | console.debug(war.clone()); 57 | -------------------------------------------------------------------------------- /chapters/chapter-3_Creational_Design_Patterns/ProxyBuilder.ts: -------------------------------------------------------------------------------- 1 | export type Builder = { 2 | [k in keyof T]-?: (arg: T[k]) => Builder; 3 | } & { 4 | build(): T; 5 | }; 6 | 7 | export function ModelBuilder(): Builder { 8 | const built: Record = {}; 9 | const builder = new Proxy( 10 | {}, 11 | { 12 | get(target, prop) { 13 | if ("build" === prop) { 14 | return () => built; 15 | } 16 | 17 | return (x: unknown): unknown => { 18 | built[prop.toString()] = x; 19 | return builder; 20 | }; 21 | }, 22 | } 23 | ); 24 | 25 | return builder as Builder; 26 | } 27 | 28 | interface UserInfo { 29 | id: number; 30 | userName: string; 31 | email: string; 32 | } 33 | 34 | const userInfo = ModelBuilder() 35 | .id(1) 36 | .userName("foo") 37 | .email("foo@bar.baz") 38 | .build(); 39 | 40 | console.debug(userInfo); 41 | -------------------------------------------------------------------------------- /chapters/chapter-3_Creational_Design_Patterns/UsersApiSingleton.ts: -------------------------------------------------------------------------------- 1 | export class UsersAPISingleton { 2 | private static instance: UsersAPISingleton; 3 | private constructor() {} 4 | static getInstance() { 5 | if (!UsersAPISingleton.instance) { 6 | UsersAPISingleton.instance = new UsersAPISingleton(); 7 | } 8 | 9 | return UsersAPISingleton.instance; 10 | } 11 | 12 | getUsers(): Promise { 13 | return Promise.resolve(["Alex", "John", "Sarah"]); 14 | } 15 | } 16 | 17 | const usersPromise = UsersAPISingleton.getInstance().getUsers(); 18 | usersPromise.then((res) => { 19 | console.log(res); 20 | }); 21 | 22 | UsersAPISingleton.getInstance("/v1/users").getUsers(); 23 | UsersAPISingleton.getInstance("/v2/users").getUsers(); 24 | 25 | UsersAPISingleton.getInstance("/v1/users") === 26 | UsersAPISingleton.getInstance("/v1/users"); 27 | UsersAPISingleton.getInstance("/v1/users") !== 28 | UsersAPISingleton.getInstance("/v2/users"); 29 | -------------------------------------------------------------------------------- /chapters/chapter-3_Creational_Design_Patterns/WebsiteBuilder.test.ts: -------------------------------------------------------------------------------- 1 | import { 2 | Website, 3 | PremiumWebsiteBuilder, 4 | WebsiteBuilder, 5 | } from "./WebsiteBuilder"; 6 | 7 | let wb: WebsiteBuilder; 8 | beforeEach(() => { 9 | wb = new PremiumWebsiteBuilder(); 10 | }); 11 | 12 | test("PremiumWebsiteBuilder builds a premium website with the correct properties", () => { 13 | const website = wb 14 | .setName("example") 15 | .setHost("localhost") 16 | .setIsPremium(false) 17 | .setPort(3000) 18 | .build(); 19 | expect(website.isPremium).toBeTruthy; 20 | expect(website.name).toBe("example"); 21 | expect(website.host).toBe("localhost"); 22 | expect(website.port).toBe(3000); 23 | }); 24 | 25 | test("PremiumWebsiteBuilder order of steps does not have side effects", () => { 26 | const website = wb 27 | .setName("example") 28 | .setPort(3000) 29 | .setHost("localhost") 30 | .setIsPremium(false) 31 | .setName("example2") 32 | .build(); 33 | expect(website.isPremium).toBeTruthy; 34 | expect(website.name).toBe("example2"); 35 | expect(website.host).toBe("localhost"); 36 | expect(website.port).toBe(3000); 37 | }); 38 | -------------------------------------------------------------------------------- /chapters/chapter-3_Creational_Design_Patterns/WebsiteBuilder.ts: -------------------------------------------------------------------------------- 1 | export class Website { 2 | constructor( 3 | public name?: string, 4 | public host?: string, 5 | public port?: number, 6 | public adminEmail?: string, 7 | public content?: string, 8 | public isPremium?: boolean 9 | ) {} 10 | } 11 | 12 | export interface WebsiteBuilder { 13 | setName(name: string): WebsiteBuilder; 14 | setHost(host: string): WebsiteBuilder; 15 | setPort(port: number): WebsiteBuilder; 16 | setAdminEmail(email: string): WebsiteBuilder; 17 | setContent(content: string): WebsiteBuilder; 18 | setIsPremium(isPremium: boolean): WebsiteBuilder; 19 | build(): Website; 20 | } 21 | 22 | export class PremiumWebsiteBuilder implements WebsiteBuilder { 23 | private website: Website; 24 | constructor() { 25 | this.website = new Website(); 26 | this.clear(); 27 | } 28 | 29 | setName(name: string): WebsiteBuilder { 30 | this.website.name = name; 31 | return this; 32 | } 33 | 34 | setHost(host: string): WebsiteBuilder { 35 | this.website.host = host; 36 | return this; 37 | } 38 | 39 | setPort(port: number): WebsiteBuilder { 40 | this.website.port = port; 41 | return this; 42 | } 43 | 44 | setAdminEmail(email: string): WebsiteBuilder { 45 | this.website.adminEmail = email; 46 | return this; 47 | } 48 | 49 | setContent(content: string): WebsiteBuilder { 50 | this.website.content = content; 51 | return this; 52 | } 53 | 54 | setIsPremium(): WebsiteBuilder { 55 | this.website.isPremium = true; 56 | return this; 57 | } 58 | 59 | build(): Website { 60 | const website = this.website; 61 | this.clear(); 62 | return website; 63 | } 64 | 65 | clear(): void { 66 | this.website = new Website(); 67 | this.website.isPremium = true; 68 | } 69 | } 70 | 71 | const wb = new PremiumWebsiteBuilder(); 72 | wb.setName("example").setHost("localhost").setPort(3000); 73 | 74 | const website = wb.build(); 75 | -------------------------------------------------------------------------------- /chapters/chapter-3_Creational_Design_Patterns/singleton.test.ts: -------------------------------------------------------------------------------- 1 | import singleton from "./singleton"; 2 | 3 | test("singleton is a Singleton", () => { 4 | expect(singleton.getInstance()).toBe(singleton.getInstance()); 5 | }); 6 | -------------------------------------------------------------------------------- /chapters/chapter-3_Creational_Design_Patterns/singleton.ts: -------------------------------------------------------------------------------- 1 | export class Singleton { 2 | // Stores the singleton instance 3 | private static instance: Singleton; 4 | // Prevents creation of new instances 5 | private constructor() {} 6 | // Method to retrieve instance 7 | static getInstance() { 8 | if (!Singleton.instance) { 9 | Singleton.instance = new Singleton(); 10 | } 11 | return Singleton.instance; 12 | } 13 | } 14 | 15 | export default Singleton; 16 | -------------------------------------------------------------------------------- /chapters/chapter-4_Structural_Design_Patterns/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PacktPublishing/TypeScript-4-Design-Patterns-and-Best-Practices/794888bd4c351b9565e01828440e3d0cf06d26c6/chapters/chapter-4_Structural_Design_Patterns/.gitkeep -------------------------------------------------------------------------------- /chapters/chapter-4_Structural_Design_Patterns/Adapter.test.ts: -------------------------------------------------------------------------------- 1 | import { ActionSender, EventAdapter } from "./Adapter"; 2 | import { mocked } from "ts-jest/utils"; 3 | import { EventCreator } from "./EventCreator"; 4 | 5 | jest.mock("./EventCreator", () => { 6 | return { 7 | EventCreator: jest.fn().mockImplementation(() => { 8 | return { 9 | sendEvent: jest.fn(), 10 | }; 11 | }), 12 | }; 13 | }); 14 | 15 | describe("EventCreator", () => { 16 | const mockedEventCreator = mocked(EventCreator, true); 17 | beforeEach(() => { 18 | // Clears the record of calls to the mock constructor function and its methods 19 | mockedEventCreator.mockClear(); 20 | }); 21 | 22 | let ad: ActionSender; 23 | test("it calls the service function", () => { 24 | ad = new EventAdapter(); 25 | ad.sendAction("action"); 26 | expect(mockedEventCreator).toHaveBeenCalledTimes(1); 27 | }); 28 | }); 29 | -------------------------------------------------------------------------------- /chapters/chapter-4_Structural_Design_Patterns/Adapter.ts: -------------------------------------------------------------------------------- 1 | import {EventCreator} from "./EventCreator"; 2 | 3 | export interface ActionSender { 4 | sendAction(action: string): Promise; 5 | } 6 | 7 | export class ActionCreator implements ActionSender { 8 | sendAction(action: string): Promise { 9 | return new Promise((resolve, reject) => { 10 | console.log("Event Created: ", action); 11 | resolve(); 12 | }); 13 | } 14 | } 15 | 16 | export interface EventSender { 17 | sendEvent(eventName: string): void; 18 | } 19 | 20 | export class EventAdapter implements ActionSender { 21 | eventSender: EventSender; 22 | constructor(eventSender: EventSender = new EventCreator()) { 23 | this.eventSender = eventSender; 24 | } 25 | public async sendAction(action: string): Promise { 26 | await this.eventSender.sendEvent(action); 27 | } 28 | } 29 | 30 | export class Client { 31 | // @ts-ignore 32 | actionCreator: ActionSender; 33 | call() { 34 | this.actionCreator = new ActionCreator(); 35 | this.actionCreator.sendAction("Hello"); 36 | this.actionCreator = new EventAdapter(new EventCreator()); 37 | this.actionCreator.sendAction("Another Action"); 38 | } 39 | } 40 | 41 | new Client().call(); 42 | -------------------------------------------------------------------------------- /chapters/chapter-4_Structural_Design_Patterns/Bridge.ts: -------------------------------------------------------------------------------- 1 | interface Box { 2 | id: string; 3 | value: string; 4 | } 5 | interface BoxArranger { 6 | arrangeItem(item: Box, items: Box[]): Box[]; 7 | } 8 | 9 | abstract class BoxContainer { 10 | constructor(public items: Box[] = [], protected boxArranger: BoxArranger) {} 11 | arrangeItem(item: Box) {} 12 | } 13 | 14 | class StraightBoxContainer extends BoxContainer { 15 | arrangeItem(item: Box) { 16 | this.items = this.boxArranger.arrangeItem(item, this.items); 17 | } 18 | } 19 | 20 | class ReversingBoxContainer extends BoxContainer { 21 | arrangeItem(item: Box) { 22 | this.items = this.boxArranger.arrangeItem(item, this.items).reverse(); 23 | } 24 | } 25 | 26 | class PutLastBoxArranger implements BoxArranger { 27 | arrangeItem(item: Box, items: Box[]): Box[] { 28 | items = items.concat(item); 29 | return items; 30 | } 31 | } 32 | 33 | class PutFirstBoxArranger implements BoxArranger { 34 | arrangeItem(item: Box, items: Box[]): Box[] { 35 | let result = items.slice(); 36 | result.unshift(item); 37 | return result; 38 | } 39 | } 40 | const items: Box[] = [ 41 | { 42 | id: "1", 43 | value: "abc", 44 | }, 45 | ]; 46 | const pfa = new PutFirstBoxArranger(); 47 | const pla = new PutLastBoxArranger(); 48 | const rv = new StraightBoxContainer(items, pla); 49 | rv.arrangeItem({ 50 | id: "3", 51 | value: "dfa", 52 | }); // [ { id: '3', value: 'dfa' }, { id: '1', value: 'abc' } ] 53 | console.log(rv.items); 54 | const sc = new StraightBoxContainer(items, pfa); 55 | sc.arrangeItem({ 56 | id: "3", 57 | value: "dfa", 58 | }); 59 | console.log(sc.items); 60 | 61 | // Implementor type 62 | interface StoreAPI { 63 | store(item: T); 64 | } 65 | // Abstraction type 66 | interface List { 67 | push(item: T): void; 68 | } 69 | 70 | class ArrayList implements List { 71 | constructor(private items: T[], private storeAPI: StoreAPI) {} 72 | push(item: T): void { 73 | this.storeAPI.store(item); 74 | } 75 | // implements methods of List 76 | } 77 | interface ListNode { 78 | data: T; 79 | next: ListNode | null; 80 | } 81 | class LinkedList implements List { 82 | constructor(private root: ListNode, private items: T[]) {} 83 | // implements methods of List 84 | push(item: T): void { 85 | const newNode: ListNode = {data: item, next: null}; 86 | newNode.next = this.root; 87 | this.root = newNode; 88 | } 89 | } 90 | -------------------------------------------------------------------------------- /chapters/chapter-4_Structural_Design_Patterns/Composite.ts: -------------------------------------------------------------------------------- 1 | interface UIElement { 2 | render(): string; 3 | } 4 | 5 | class WindowComposite implements UIElement { 6 | private children: UIElement[]; 7 | 8 | constructor() { 9 | this.children = []; 10 | } 11 | 12 | render(): string { 13 | let result = ""; 14 | for (let i = 0; i < this.children.length; i += 1) { 15 | result += this.children[i].render(); 16 | } 17 | result += ""; 18 | return result; 19 | } 20 | addComponent(c: UIElement): void { 21 | this.children.push(c); 22 | } 23 | removeComponent(idx: number): void { 24 | if (this.children.length <= idx) { 25 | throw new Error("index out of bound!"); 26 | } 27 | this.children.splice(idx, 1); 28 | } 29 | } 30 | 31 | class Button implements UIElement { 32 | constructor(private text: string) {} 33 | render(): string { 34 | return ``; 35 | } 36 | } 37 | const wc: WindowComposite = new WindowComposite(); 38 | wc.addComponent(new Button("Click me")); 39 | wc.addComponent(new Button("No Click me")); 40 | console.info(wc.render()); // 41 | -------------------------------------------------------------------------------- /chapters/chapter-4_Structural_Design_Patterns/Decorator.test.ts: -------------------------------------------------------------------------------- 1 | import { LogEventDecorator, Event } from "./Decorator"; 2 | const spy = jest.spyOn(console, "log").mockImplementation(() => {}); 3 | 4 | afterEach(() => { 5 | spy.mockReset(); 6 | }); 7 | 8 | test("it calls the decorated object method", () => { 9 | const mockSendEvent = jest.fn(); 10 | const mock = { 11 | send: mockSendEvent, 12 | }; 13 | const log = new LogEventDecorator(mock); 14 | log.send("example"); 15 | expect(mockSendEvent).toHaveBeenCalledWith("example"); 16 | }); 17 | 18 | test("it calls the decorator calls before and after the call to decorated method", () => { 19 | const mockSendEvent = jest.fn(); 20 | const mock = { 21 | send: mockSendEvent, 22 | }; 23 | const log = new LogEventDecorator(mock); 24 | log.send("example"); 25 | expect(spy).toBeCalledTimes(2); 26 | }); 27 | -------------------------------------------------------------------------------- /chapters/chapter-4_Structural_Design_Patterns/Decorator.ts: -------------------------------------------------------------------------------- 1 | export interface Event { 2 | send(message: string): void; 3 | } 4 | 5 | export class SendEmailEvent implements Event { 6 | public send(message: string): void { 7 | // send event using email client 8 | console.log("Currently sending event message", message); 9 | } 10 | } 11 | 12 | export class LogEventDecorator implements Event { 13 | constructor(private event: Event) { 14 | this.event = event; 15 | } 16 | 17 | public send(message: string): void { 18 | console.log("Before sending event message", message); 19 | this.event.send(message); // forward call to event 20 | console.log("After sending event message: ", message); 21 | } 22 | } 23 | 24 | const sendEmail: Event = new SendEmailEvent(); 25 | const logSendEmail = new LogEventDecorator(sendEmail); 26 | logSendEmail.send("Hi!"); 27 | 28 | function logCall( 29 | target: any, 30 | propertyKey: string, 31 | descriptor: PropertyDescriptor 32 | ) {} 33 | 34 | function LogCall() { 35 | return function ( 36 | target: Object, 37 | key: string | symbol, 38 | descriptor: PropertyDescriptor 39 | ) { 40 | const caller = descriptor.value; 41 | descriptor.value = (message: string) => { 42 | console.log("Before sending event message", message); 43 | // @ts-ignore 44 | caller.apply(this, [message]); 45 | console.log("After sending event message", message); 46 | return caller; 47 | }; 48 | return descriptor; 49 | }; 50 | } 51 | 52 | class EventService { 53 | @LogCall() 54 | createEvent(message: string): void { 55 | console.log("Currently sending event message", message); 56 | } 57 | } 58 | 59 | new EventService().createEvent("Message"); 60 | -------------------------------------------------------------------------------- /chapters/chapter-4_Structural_Design_Patterns/EventCreator.ts: -------------------------------------------------------------------------------- 1 | import {EventSender} from "./Adapter"; 2 | 3 | export class EventCreator implements EventSender { 4 | sendEvent(action: string): void { 5 | console.log("Event Created: ", action); 6 | } 7 | } -------------------------------------------------------------------------------- /chapters/chapter-4_Structural_Design_Patterns/Facade.ts: -------------------------------------------------------------------------------- 1 | class Facade { 2 | constructor(private serviceA: ServiceA, private serviceB: ServiceB) {} 3 | 4 | perform() { 5 | this.serviceA.action(); 6 | this.serviceB.action(); 7 | // more complex logic here 8 | } 9 | } 10 | interface ServiceA { 11 | action(): void; 12 | } 13 | interface ServiceB { 14 | action(): void; 15 | } 16 | 17 | class ServiceAImpl implements ServiceA { 18 | action(): void { 19 | console.log("Performing action on Service A"); 20 | } 21 | } 22 | 23 | class ServiceBImpl implements ServiceB { 24 | action(): void { 25 | console.log("Performing action on Service B"); 26 | } 27 | } 28 | 29 | new Facade(new ServiceAImpl(), new ServiceBImpl()).perform(); 30 | -------------------------------------------------------------------------------- /chapters/chapter-4_Structural_Design_Patterns/FacadeExample.ts: -------------------------------------------------------------------------------- 1 | class DocumentService { 2 | create(name: string): Promise { 3 | return Promise.resolve(`Location: /documents/${name}`); 4 | } 5 | } 6 | interface ApiClient { 7 | upload(url: string, blob: Blob): Promise; 8 | } 9 | 10 | class UploadService { 11 | constructor(private client: ApiClient) {} 12 | upload(url: string, blob: Blob): Promise { 13 | return this.client.upload(url, blob); 14 | } 15 | } 16 | class DocumentUploadFacade { 17 | constructor( 18 | private documentService: DocumentService, 19 | private uploadService: UploadService 20 | ) {} 21 | 22 | async createAndUploadDocument(name: string, blob: Blob): Promise { 23 | let path: string | null = null; 24 | let status = false; 25 | try { 26 | path = await this.documentService.create(name); 27 | } catch (e) { 28 | console.error(e); 29 | } 30 | if (path) { 31 | try { 32 | status = await this.uploadService.upload(path, blob); 33 | } catch (e) { 34 | console.error(e); 35 | } 36 | } 37 | 38 | return status; 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /chapters/chapter-4_Structural_Design_Patterns/Flyweight.ts: -------------------------------------------------------------------------------- 1 | export interface Flyweight { 2 | perform(customization: { id: string }): void; 3 | } 4 | 5 | export class ConcreteFlyweight implements Flyweight { 6 | constructor(private sharedState: Object) {} 7 | 8 | public perform(customization: { id: string }): void { 9 | console.log( 10 | `Call to ConcreteFlyweight with param: ${customization.id} called` 11 | ); 12 | } 13 | } 14 | 15 | export class FlyweightFactory { 16 | private cache = new Map(); 17 | public create(sharedState: Object): Flyweight { 18 | if (!this.cache.has(sharedState)) { 19 | this.cache.set(sharedState, new ConcreteFlyweight(sharedState)); 20 | } 21 | return this.cache.get(sharedState)!; 22 | } 23 | } 24 | 25 | new FlyweightFactory().create({ state: "Initial" }).perform({ id: "abc" }); 26 | -------------------------------------------------------------------------------- /chapters/chapter-4_Structural_Design_Patterns/Proxy.es6.ts: -------------------------------------------------------------------------------- 1 | const textStore = { 2 | save(data: string): void { 3 | console.log(`Called 'save' from TextStore with data=${data}`); 4 | }, 5 | }; 6 | 7 | const proxyTextStore = new Proxy(textStore, { 8 | apply: function (target, that, args) { 9 | console.log(`Called 'save' from ProxyTextStore with data=${args}`); 10 | target.save(args[0]); 11 | }, 12 | }); 13 | proxyTextStore.save("Data"); 14 | -------------------------------------------------------------------------------- /chapters/chapter-4_Structural_Design_Patterns/Proxy.ts: -------------------------------------------------------------------------------- 1 | export interface Store { 2 | save(data: string): void; 3 | } 4 | 5 | export class TextStore implements Store { 6 | save(data: string): void { 7 | console.log(`Called 'save' from TextStore with data=${data}`); 8 | } 9 | } 10 | 11 | export class ProxyTextStore implements Store { 12 | constructor(private textStore?: TextStore) {} 13 | save(data: string): void { 14 | console.log(`Called 'save' from ProxyTextStore with data=${data}`); 15 | if (!this.textStore) { 16 | console.log("Lazy init: textStore."); 17 | this.textStore = new TextStore(); 18 | } 19 | this.textStore.save(data); 20 | } 21 | } 22 | 23 | new ProxyTextStore().save("Data"); 24 | //Called 'save' from ProxyTextStore with data=Data 25 | //Lazy init: textStore. 26 | //Called 'save' from TextStore with data=Data 27 | -------------------------------------------------------------------------------- /chapters/chapter-5_Behavioral_Design_Patterns/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PacktPublishing/TypeScript-4-Design-Patterns-and-Best-Practices/794888bd4c351b9565e01828440e3d0cf06d26c6/chapters/chapter-5_Behavioral_Design_Patterns/.gitkeep -------------------------------------------------------------------------------- /chapters/chapter-5_Behavioral_Design_Patterns/ChainOfResponsibility.test.ts: -------------------------------------------------------------------------------- 1 | import { HTTPRequest, LogRequestHandler } from "./ChainOfResponsibility"; 2 | const spy = jest.spyOn(console, "log").mockImplementation(); 3 | afterAll(() => { 4 | spy.mockRestore(); 5 | }); 6 | 7 | test("HTTPRequest", () => { 8 | const req = new HTTPRequest("localhost", new Map().set("q", "searchTerm")); 9 | expect(req.getOrigin()).toBe("localhost"); 10 | expect(req.getParams()).toEqual(new Map().set("q", "searchTerm")); 11 | }); 12 | 13 | test("LogRequestHandler", () => { 14 | const req = new HTTPRequest("localhost", new Map().set("q", "searchTerm")); 15 | const requestHandler = new LogRequestHandler(null); 16 | requestHandler.handleRequest(req); 17 | expect(spy).toHaveBeenCalledTimes(1); 18 | }); 19 | -------------------------------------------------------------------------------- /chapters/chapter-5_Behavioral_Design_Patterns/ChainOfResponsibility.ts: -------------------------------------------------------------------------------- 1 | export interface IRequest { 2 | getOrigin(): string; 3 | getParams(): Map; 4 | } 5 | 6 | export class HTTPRequest implements IRequest { 7 | constructor(private origin: string, private params: Map) {} 8 | getOrigin(): string { 9 | return this.origin; 10 | } 11 | getParams(): Map { 12 | return this.params; 13 | } 14 | } 15 | 16 | export abstract class RequestHandler { 17 | constructor(protected next: RequestHandler | null) {} 18 | 19 | handleRequest(request: IRequest) { 20 | if (this.next !== null) { 21 | this.next.handleRequest(request); 22 | } 23 | } 24 | } 25 | export class LogRequestHandler extends RequestHandler { 26 | handleRequest(request: IRequest) { 27 | console.log( 28 | `Request with origin: ${request.getOrigin()} handled by LogRequestHandler` 29 | ); 30 | if (this.next !== null) { 31 | this.next.handleRequest(request); 32 | } 33 | } 34 | } 35 | export class EmailRequestHandler extends RequestHandler { 36 | handleRequest(request: IRequest) { 37 | console.log( 38 | `Request with origin: ${request.getOrigin()} handled by EmailRequestHandler` 39 | ); 40 | if (this.next !== null) { 41 | this.next.handleRequest(request); 42 | } 43 | } 44 | } 45 | const req = new HTTPRequest("localhost", new Map().set("q", "searchTerm")); 46 | new LogRequestHandler(new EmailRequestHandler(null)).handleRequest(req); // 47 | // Request with origin: localhost handled by LogRequestHandler 48 | // Request with origin: localhost handled by EmailRequestHandler 49 | -------------------------------------------------------------------------------- /chapters/chapter-5_Behavioral_Design_Patterns/Command.ts: -------------------------------------------------------------------------------- 1 | interface Command { 2 | execute(); 3 | } 4 | interface Receiver { 5 | methodA(); 6 | methodB(); 7 | } 8 | class ConcreteCommandA implements Command { 9 | constructor(private receiver: Receiver) {} 10 | 11 | execute() { 12 | this.receiver.methodA(); 13 | } 14 | } 15 | class ConcreteCommandB implements Command { 16 | constructor(private receiver: Receiver) {} 17 | 18 | execute() { 19 | this.receiver.methodB(); 20 | } 21 | } 22 | 23 | interface CommandHandler { 24 | handle(command: Command); 25 | } 26 | 27 | class ConcreteCommandHandler implements CommandHandler { 28 | private commands: Command[] = []; 29 | handle(command: Command) { 30 | command.execute(); 31 | this.commands.push(command); 32 | } 33 | } 34 | 35 | class ConcreteReceiver implements Receiver { 36 | methodA() { 37 | console.log("Called method A"); 38 | } 39 | methodB() { 40 | console.log("Called method B"); 41 | } 42 | } 43 | 44 | const handler = new ConcreteCommandHandler(); 45 | const receiver = new ConcreteReceiver(); 46 | handler.handle(new ConcreteCommandA(receiver)); // logs Called method A 47 | handler.handle(new ConcreteCommandB(receiver)); // logs Called method B 48 | -------------------------------------------------------------------------------- /chapters/chapter-5_Behavioral_Design_Patterns/Iterator.ts: -------------------------------------------------------------------------------- 1 | export interface Iterator { 2 | next(): T | null; 3 | hasNext(): boolean; 4 | } 5 | class ListNode { 6 | constructor(public next: ListNode | null, public data: T) {} 7 | } 8 | class ListIterator implements Iterator { 9 | constructor(private root: ListNode | null) {} 10 | next(): T | null { 11 | if (this.hasNext()) { 12 | const data = this.root!.data; 13 | this.root = this.root!.next; 14 | return data; 15 | } 16 | return null; 17 | } 18 | hasNext(): boolean { 19 | return this.root !== null; 20 | } 21 | } 22 | 23 | class ListAggregate { 24 | constructor(private rootList: ListNode) {} 25 | 26 | getListIterator(): ListIterator { 27 | return new ListIterator(this.rootList); 28 | } 29 | } 30 | const list = new ListNode(new ListNode(new ListNode(null, 10), 5), 15); 31 | const aggregate = new ListAggregate(list); 32 | const iterator = aggregate.getListIterator(); 33 | while (iterator.hasNext()) { 34 | console.log(iterator.next()); // prints 15, 5, 10 35 | } 36 | class OnlyA implements Iterable { 37 | constructor(private limit: number = 3) {} 38 | 39 | [Symbol.iterator]() { 40 | let limit = this.limit; 41 | 42 | return { 43 | next(): IteratorResult { 44 | return { 45 | done: limit-- === 0, 46 | value: "A", 47 | }; 48 | }, 49 | }; 50 | } 51 | } 52 | const a = new OnlyA(); 53 | for (let i of a) { 54 | console.log(i); 55 | } 56 | -------------------------------------------------------------------------------- /chapters/chapter-5_Behavioral_Design_Patterns/Mediator.ts: -------------------------------------------------------------------------------- 1 | interface WorkerMediator { 2 | triggerEvent(sender: object, message: string): void; 3 | } 4 | 5 | class WorkerCenter implements WorkerMediator { 6 | constructor( 7 | protected workerA: BatchWorker, 8 | protected workerB: SingleTaskWorker 9 | ) { 10 | this.workerA.setMediator(this); 11 | this.workerB.setMediator(this); 12 | } 13 | 14 | public triggerEvent(sender: object, message: string): void { 15 | // Triggers stack overflow error 16 | // if (message.startsWith("single_job_completed")) { 17 | // this.workerB.performWork(); 18 | // } 19 | 20 | if (message.startsWith("batch_job_completed")) { 21 | this.workerB.performWork(); 22 | } 23 | } 24 | } 25 | 26 | abstract class Workhorse { 27 | protected mediator: WorkerMediator | undefined; 28 | constructor(mediator?: WorkerMediator) { 29 | this.mediator = mediator; 30 | } 31 | 32 | setMediator(mediator: WorkerMediator): void { 33 | this.mediator = mediator; 34 | } 35 | } 36 | 37 | class BatchWorker extends Workhorse { 38 | public performWork(): void { 39 | console.log("Performing work in BatchWorker"); 40 | if (this.mediator) { 41 | this.mediator.triggerEvent(this, "batch_job_completed"); 42 | } 43 | } 44 | 45 | public finalize(): void { 46 | console.log("Performing final work in BatchWorker"); 47 | if (this.mediator) { 48 | this.mediator.triggerEvent(this, "final_job_completed"); 49 | } 50 | } 51 | } 52 | 53 | class SingleTaskWorker extends Workhorse { 54 | public performWork(): void { 55 | console.log("Performing work in SingleTaskWorker"); 56 | if (this.mediator) { 57 | this.mediator.triggerEvent(this, "single_job_completed"); 58 | } 59 | } 60 | } 61 | 62 | const workerA = new BatchWorker(); 63 | const workerB = new SingleTaskWorker(); 64 | const mediator = new WorkerCenter(workerA, workerB); 65 | 66 | workerA.performWork(); 67 | -------------------------------------------------------------------------------- /chapters/chapter-5_Behavioral_Design_Patterns/Memento.ts: -------------------------------------------------------------------------------- 1 | interface AppState { 2 | data: any; 3 | } 4 | 5 | abstract class Memento { 6 | constructor(protected state: AppState) {} 7 | getState(): AppState { 8 | return this.state; 9 | } 10 | } 11 | 12 | class ConcreteMemento extends Memento { 13 | getState(): AppState { 14 | return super.getState(); 15 | } 16 | } 17 | 18 | class Originator { 19 | constructor(private state: AppState) {} 20 | save(): Memento { 21 | return new ConcreteMemento(this.state); 22 | } 23 | restore(memento: Memento): void { 24 | this.state = memento.getState(); 25 | } 26 | } 27 | 28 | export class CareTaker { 29 | constructor( 30 | private originator: Originator, 31 | private mementos: Memento[] = [] 32 | ) {} 33 | 34 | restoreLastMemento() { 35 | if (this.mementos.length === 0) { 36 | return; 37 | } 38 | const memento = this.mementos.pop()!; 39 | this.originator.restore(memento); 40 | } 41 | 42 | saveMemento() { 43 | this.mementos.push(this.originator.save()); 44 | } 45 | } 46 | const state: AppState = { 47 | data: "paste data", 48 | }; 49 | const originator = new Originator(state); 50 | const caretaker = new CareTaker(originator); 51 | console.log("Originator data:", originator.save().getState().data); // Originator data: paste data 52 | 53 | state.data = "many more data"; 54 | caretaker.saveMemento(); 55 | caretaker.restoreLastMemento(); 56 | console.log("Restored data:", originator.save().getState().data); // Restored data: many more data 57 | 58 | // interface EditorState {} 59 | // class Editor { 60 | // constructor(private state: EditorState) {} 61 | // public save(): Revision { 62 | // return new Revision(this.state); 63 | // } 64 | // public restore(revision: Revision): void { 65 | // this.state = revision.getState(); 66 | // } 67 | // } 68 | 69 | // class Revision { 70 | // constructor(private state: EditorState) {} 71 | // } 72 | 73 | // class RevisionList { 74 | // private revisions: Revision[] = []; 75 | // constructor(private editor: Editor) {} 76 | // } 77 | -------------------------------------------------------------------------------- /chapters/chapter-5_Behavioral_Design_Patterns/Observer.ts: -------------------------------------------------------------------------------- 1 | export interface Subscriber { 2 | notify(): void; 3 | } 4 | 5 | export abstract class Subject { 6 | private subscribers: Subscriber[] = []; 7 | 8 | public addSubscriber(s: Subscriber): void { 9 | this.subscribers.push(s); 10 | } 11 | 12 | public removeSubscriber(s: Subscriber): void { 13 | var n: number = this.subscribers.indexOf(s); 14 | this.subscribers.splice(n, 1); 15 | } 16 | 17 | public notify(): void { 18 | console.log("notifying all the subscriber list"); 19 | for (let s of this.subscribers) { 20 | s.notify(); 21 | } 22 | } 23 | } 24 | 25 | export class ConcreteSubject extends Subject { 26 | private state: any; 27 | 28 | getState(): any { 29 | return this.state; 30 | } 31 | 32 | setState(state: any) { 33 | this.state = state; 34 | } 35 | } 36 | 37 | export class ConcreteSubscriber implements Subscriber { 38 | private state: any; 39 | 40 | constructor(private subject: ConcreteSubject) {} 41 | 42 | public notify(): void { 43 | this.state = this.subject.getState(); 44 | console.log( 45 | "ConcreteSubscriber: Received notify method from subject state", 46 | this.state 47 | ); 48 | } 49 | 50 | getSubject(): ConcreteSubject { 51 | return this.subject; 52 | } 53 | 54 | setSubject(subject: ConcreteSubject) { 55 | this.subject = subject; 56 | } 57 | } 58 | 59 | const subject = new ConcreteSubject(); 60 | const subA = new ConcreteSubscriber(subject); 61 | subject.addSubscriber(subA); 62 | 63 | const subB = new ConcreteSubscriber(subject); 64 | subject.addSubscriber(subB); 65 | 66 | subject.setState(19); 67 | subject.notify(); 68 | -------------------------------------------------------------------------------- /chapters/chapter-5_Behavioral_Design_Patterns/State.test.ts: -------------------------------------------------------------------------------- 1 | import { Originator, PassiveState, ActiveState } from "./State"; 2 | const spy = jest.spyOn(console, "log").mockImplementation(); 3 | afterAll(() => { 4 | spy.mockRestore(); 5 | }); 6 | 7 | test("Originator state is Passive by default", () => { 8 | const origin = new Originator(); 9 | origin.reportState(); 10 | 11 | expect(spy).toHaveBeenCalledWith("Originator is in passive mode currently"); 12 | }); 13 | 14 | test("Originator state can transition to a new state", () => { 15 | const origin = new Originator(); 16 | const newState = new ActiveState(); 17 | origin.changeState(newState); 18 | origin.reportState(); 19 | 20 | expect(spy).toHaveBeenCalledWith("Originator is in active mode currently"); 21 | }); 22 | -------------------------------------------------------------------------------- /chapters/chapter-5_Behavioral_Design_Patterns/State.ts: -------------------------------------------------------------------------------- 1 | interface State { 2 | onEnterState(): void; 3 | reportState(): void; 4 | data: any; 5 | } 6 | 7 | export class PassiveState implements State { 8 | data: any; 9 | constructor() { 10 | this.data = "Passive data"; 11 | } 12 | 13 | reportState(): void { 14 | console.log("Originator is in passive mode currently"); 15 | } 16 | 17 | onEnterState(): void { 18 | console.log("Originator changed into passing mode"); 19 | } 20 | } 21 | 22 | export class ActiveState implements State { 23 | data: any; 24 | constructor() { 25 | this.data = "Active data"; 26 | } 27 | 28 | reportState(): void { 29 | console.log("Originator is in active mode currently"); 30 | } 31 | 32 | onEnterState(): void { 33 | console.log("Originator changed into active mode"); 34 | } 35 | } 36 | 37 | export class Originator { 38 | private state: State; 39 | constructor() { 40 | this.state = new PassiveState(); 41 | } 42 | 43 | changeState(state: State): void { 44 | this.state = state; 45 | this.state.onEnterState(); 46 | } 47 | 48 | reportState(): void { 49 | this.state.reportState(); 50 | } 51 | } 52 | // const originator = new Originator(); 53 | // originator.reportState(); // Originator is in passive mode currently 54 | 55 | // originator.changeState(new ActiveState()); // Originator changed into active mode 56 | // originator.reportState(); // Originator is in active mode currently 57 | -------------------------------------------------------------------------------- /chapters/chapter-5_Behavioral_Design_Patterns/StateButton.ts: -------------------------------------------------------------------------------- 1 | interface ButtonState { 2 | status: "loading" | "disabled" | "active"; 3 | text: string; 4 | icon: string; 5 | } 6 | 7 | class ActiveButtonState implements ButtonState { 8 | status: ButtonState["status"] = "active"; 9 | text = "Click me"; 10 | icon = "click_me_icon"; 11 | constructor(private originator: Button) {} 12 | } 13 | 14 | export class Button { 15 | private state: ButtonState; 16 | 17 | constructor() { 18 | this.state = new ActiveButtonState(this); 19 | } 20 | 21 | changeState(state: ButtonState) { 22 | this.state = state; 23 | } 24 | 25 | render(): string { 26 | const { text, icon, status } = this.state; 27 | let disabled = false; 28 | if (status === "loading") { 29 | disabled = true; 30 | } 31 | return ``; 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /chapters/chapter-5_Behavioral_Design_Patterns/Strategy.ts: -------------------------------------------------------------------------------- 1 | interface BillingStrategy { 2 | calculate(): void; 3 | } 4 | 5 | class ConcreteBillingStrategyA implements BillingStrategy { 6 | calculate(): void { 7 | console.log("Calculating bill using first strategy"); 8 | } 9 | } 10 | 11 | class ConcreteBillingStrategyB implements BillingStrategy { 12 | calculate(): void { 13 | console.log("Calculating bill using second strategy"); 14 | } 15 | } 16 | 17 | class BillingContext { 18 | constructor(private strategy: BillingStrategy) {} 19 | 20 | setStrategy(strategy: BillingStrategy) { 21 | this.strategy = strategy; 22 | } 23 | 24 | calculateBill(): void { 25 | this.strategy.calculate(); 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /chapters/chapter-5_Behavioral_Design_Patterns/TemplateMethod.ts: -------------------------------------------------------------------------------- 1 | abstract class ShippingItemsTemplate { 2 | shipItems(items: string[]): void { 3 | this.prepareItems(items); 4 | this.performShipment(items); 5 | this.onItemsShipped(items); 6 | } 7 | protected prepareItems(items: string[]): void { 8 | console.log("Preparing items to be shipped to destination"); 9 | } 10 | protected abstract performShipment(items: string[]): void; 11 | protected onItemsShipped(items: string[]): void {} 12 | } 13 | 14 | class AirShipment extends ShippingItemsTemplate { 15 | protected performShipment(): void { 16 | console.log("Shipping items by Air"); 17 | } 18 | protected onItemsShipped(items: string[]): void { 19 | console.log("Items shipped by Air. Expect quick arrival."); 20 | } 21 | } 22 | 23 | class LandShipment extends ShippingItemsTemplate { 24 | protected performShipment(items: string[]): void { 25 | console.log("Shipping items by land"); 26 | } 27 | protected onItemsShipped(items: string[]): void { 28 | console.log("Items shipped by land. Expect slow arrival."); 29 | } 30 | } 31 | 32 | const airShipment: ShippingItemsTemplate = new AirShipment(); 33 | const landShipment: ShippingItemsTemplate = new LandShipment(); 34 | airShipment.shipItems(["Chips", "Motherboards"]); 35 | landShipment.shipItems(["Chips", "Motherboards"]); 36 | -------------------------------------------------------------------------------- /chapters/chapter-5_Behavioral_Design_Patterns/Visitor.ts: -------------------------------------------------------------------------------- 1 | export interface DocumentVisitor { 2 | visitConcreteDocumentA(concreteDocumentA: ConcreteDocumentA): void; 3 | visitConcreteDocumentB(concreteDocumentB: ConcreteDocumentB): void; 4 | } 5 | export interface AcceptsVisitor { 6 | accept(visitor: DocumentVisitor): void; 7 | } 8 | export class ConcreteDocumentA implements AcceptsVisitor { 9 | accept(visitor: DocumentVisitor): void { 10 | visitor.visitConcreteDocumentA(this); 11 | } 12 | } 13 | export class ConcreteDocumentB implements AcceptsVisitor { 14 | accept(visitor: DocumentVisitor): void { 15 | visitor.visitConcreteDocumentB(this); 16 | } 17 | } 18 | export class ConcreteDocumentVisitor implements DocumentVisitor { 19 | visitConcreteDocumentA(concreteDocumentA: ConcreteDocumentA): void { 20 | console.log("ConcreteDocumentVisitor visits ConcreteDocumentA"); 21 | } 22 | 23 | visitConcreteDocumentB(concreteDocumentB: ConcreteDocumentB): void { 24 | console.log("ConcreteDocumentVisitor visits ConcreteDocumentB"); 25 | } 26 | } 27 | export class CompositeDocument implements AcceptsVisitor { 28 | private documents: AcceptsVisitor[] = []; 29 | 30 | public addDocument(d: AcceptsVisitor): void { 31 | this.documents.push(d); 32 | } 33 | public accept(visitor: DocumentVisitor): void { 34 | for (let document of this.documents) { 35 | document.accept(visitor); 36 | } 37 | } 38 | } 39 | 40 | const composite = new CompositeDocument(); 41 | const visitor = new ConcreteDocumentVisitor(); 42 | composite.addDocument(new ConcreteDocumentA()); 43 | composite.addDocument(new ConcreteDocumentB()); 44 | composite.accept(visitor); 45 | -------------------------------------------------------------------------------- /chapters/chapter-6_Functional_Programming_Concepts/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PacktPublishing/TypeScript-4-Design-Patterns-and-Best-Practices/794888bd4c351b9565e01828440e3d0cf06d26c6/chapters/chapter-6_Functional_Programming_Concepts/.gitkeep -------------------------------------------------------------------------------- /chapters/chapter-6_Functional_Programming_Concepts/FirstClassCitizens.ts: -------------------------------------------------------------------------------- 1 | const mod = function (a: number, b: number): number { 2 | return a % b; 3 | }; 4 | 5 | function calculateMod( 6 | a: number, 7 | b: number, 8 | modFun: (a: number, b: number) => number 9 | ): number { 10 | return modFun(a, b); 11 | } 12 | console.log(calculateMod(10, 3, mod)); // 1 13 | 14 | function mulByTwo(a: number): () => number { 15 | return () => { 16 | return a * 2; 17 | }; 18 | } 19 | 20 | mulByTwo(4)(); // 8 21 | -------------------------------------------------------------------------------- /chapters/chapter-6_Functional_Programming_Concepts/FunctionComposition.ts: -------------------------------------------------------------------------------- 1 | import * as R from "ramda"; 2 | 3 | function toLowerCase(input: string): string { 4 | return input.toLocaleLowerCase(); 5 | } 6 | 7 | function substring(input: string, index: number): string { 8 | return input.substr(0, index); 9 | } 10 | 11 | function partitionAt(input: string, index: number): [string, string?] { 12 | if (index < 0 || index > input.length) { 13 | return [input]; 14 | } 15 | return [input.substr(0, index), input.substr(index)]; 16 | } 17 | 18 | console.log(partitionAt(toLowerCase("aAJu234AA*AUHF"), 4)); 19 | // toLowerCase(partitionAt("aAJu234AA*AUHF"), 4) // return type does not match with input type 20 | 21 | const add = (a: number, b: number) => a + b; 22 | // const add = (a: number) => (b: number) => a + b; 23 | 24 | const addTwoNumbers = (a: number, b: number) => a + b; 25 | 26 | const addTwoNumbersC = R.curry(addTwoNumbers); 27 | const f = addTwoNumbersC(4); 28 | console.log(f(3)); //7 29 | 30 | const message = R.compose(toLowerCase, substring); 31 | 32 | console.log(message("aAJu234AA*AUHF", 4)); 33 | 34 | function sortList(list: number[]): number[] { 35 | list.sort(); 36 | return list; 37 | } 38 | let list = [42, 1, 4, 5]; 39 | let l = [...sortList(list)]; 40 | let m = [...list]; 41 | console.log(l); 42 | console.log(m); 43 | -------------------------------------------------------------------------------- /chapters/chapter-6_Functional_Programming_Concepts/Immutability.ts: -------------------------------------------------------------------------------- 1 | interface BlogPost { 2 | title: string; 3 | tags: string[]; 4 | } 5 | 6 | //post.title = "Welcome to Javascript"; 7 | 8 | /* 9 | { 10 | title: 'Welcome to Typescript', 11 | tags: [ 'typescript', 'learning', 'example' ] 12 | } 13 | */ 14 | 15 | type Primitive = undefined | null | boolean | string | number | Function; 16 | export type DeepReadonly = T extends Primitive 17 | ? T 18 | : T extends Array 19 | ? ImmutableArray 20 | : T extends Map 21 | ? ImmutableMap 22 | : T extends Set 23 | ? ImmutableSet 24 | : ImmutableObject; 25 | 26 | export type ImmutableArray = ReadonlyArray>; 27 | export type ImmutableMap = ReadonlyMap, DeepReadonly>; 28 | export type ImmutableSet = ReadonlySet>; 29 | export type ImmutableObject = { 30 | readonly [K in keyof T]: DeepReadonly; 31 | }; 32 | 33 | const post: DeepReadonly = { 34 | title: "Welcome to Typescript", 35 | tags: ["typescript", "learning"], 36 | }; 37 | 38 | (post.tags as string[]).push("demo"); // works 39 | 40 | import { List } from "immutable"; 41 | 42 | interface ImmutableBlogPost { 43 | title: string; 44 | tags: List; 45 | } 46 | 47 | const anotherPost: ImmutableBlogPost = { 48 | title: "Welcome to Typescript", 49 | tags: List(["typescript", "learning"]), 50 | }; 51 | (anotherPost.tags as any).push("demo"); 52 | console.log(anotherPost.tags.toArray()); // [ 'typescript', 'learning' ] 53 | -------------------------------------------------------------------------------- /chapters/chapter-6_Functional_Programming_Concepts/Lens.ts: -------------------------------------------------------------------------------- 1 | export interface Lens { 2 | get: (obj: T) => A; 3 | set: (obj: T) => (newValue: A) => T; 4 | } 5 | function lensProp(key: string): Lens { 6 | return { 7 | get: (obj: T): A => obj[key], 8 | set: 9 | (obj: T) => 10 | (value: A): T => ({ ...obj, [key]: value }), 11 | }; 12 | } 13 | 14 | interface User { 15 | name: string; 16 | email: string; 17 | address: { 18 | street: string; 19 | number: string; 20 | city: string; 21 | country: string; 22 | }; 23 | } 24 | 25 | const nameLens: Lens = lensProp("name"); 26 | const user: User = { 27 | name: "Theo", 28 | email: "theo@example.com", 29 | address: { 30 | street: "Pembroke ST", 31 | number: "22", 32 | city: "Dublin", 33 | country: "Ireland", 34 | }, 35 | }; 36 | console.log(nameLens.get(user)); // Theo 37 | console.log(nameLens.set(user)("Alex")); 38 | console.log(nameLens.get(user)); // Theo 39 | 40 | function view(lens: Lens, obj: T): A { 41 | return lens.get(obj); 42 | } 43 | 44 | function set(lens: Lens, obj: T, value: A): T { 45 | return lens.set(obj)(value); 46 | } 47 | 48 | function over(lens: Lens, f: (x: A) => A, obj: T) { 49 | return lens.set(obj)(f(lens.get(obj))); 50 | } 51 | 52 | const prefixedName: User = over( 53 | nameLens, 54 | (name: string) => `Mr. ${name}`, 55 | user 56 | ); 57 | console.log(view(nameLens, prefixedName)); // Mr. Theo 58 | 59 | interface TodoItem { 60 | id: string; 61 | title: string; 62 | completed: boolean; 63 | } 64 | interface TodoListState { 65 | allItemIds: string[]; 66 | byItemId: { id: string; item: TodoItem }; 67 | } 68 | 69 | interface UpdateTodoItemCompletedAction { 70 | type: "UPDATE_TODO_ITEM_COMPLETED"; 71 | id: string; 72 | completed: boolean; 73 | } 74 | function reduceState( 75 | currentState: TodoListState, 76 | action: UpdateTodoItemCompletedAction 77 | ): TodoListState { 78 | switch (action.type) { 79 | case "UPDATE_TODO_ITEM_COMPLETED": 80 | return { 81 | ...currentState, 82 | byItemId: { 83 | ...currentState.byItemId, 84 | [action.id]: { 85 | ...currentState.byItemId[action.id], 86 | completed: action.completed, 87 | }, 88 | }, 89 | }; 90 | } 91 | } 92 | -------------------------------------------------------------------------------- /chapters/chapter-6_Functional_Programming_Concepts/Monad.ts: -------------------------------------------------------------------------------- 1 | import { pipe, flatten } from "ramda"; 2 | import * as A from "fp-ts/ReadonlyArray"; 3 | import { pipe as p, flow } from "fp-ts/function"; 4 | import * as O from "fp-ts/Option"; 5 | import { some } from "fp-ts/lib/ReadonlyRecord"; 6 | 7 | function add2(x: number): number { 8 | return x + 2; 9 | } 10 | 11 | function mul3(x: number): number { 12 | return x * 3; 13 | } 14 | 15 | console.log(mul3(add2(2))); // (2 + 2) * 3 = 12 16 | console.log(add2(mul3(2))); // (2 * 3) + 2 = 8 17 | 18 | type Optional = Some | None; 19 | type None = { 20 | isEmpty(): true; 21 | map(f: (value: null) => U): None; 22 | flatMap(f: (value: null) => Optional): None; 23 | }; 24 | type Some = { 25 | get(): T; 26 | isEmpty(): false; 27 | map(f: (value: T) => null): None; 28 | map(f: (value: T) => U): Some; 29 | map(f: (value: T) => U): Optional; 30 | 31 | flatMap(f: (value: T) => Some): Some; 32 | flatMap(f: (value: T) => None): None; 33 | flatMap(f: (value: T) => Optional): Optional; 34 | }; 35 | const None: None = { 36 | isEmpty: () => true, 37 | map: (f: (value: never) => T) => None, 38 | flatMap: (f: (value: never) => Optional) => None, 39 | }; 40 | 41 | function Some(value: T): Some { 42 | return { 43 | get: () => value, 44 | isEmpty: () => false, 45 | map: (f: (value: T) => U) => Optional(f(value)) as any, 46 | flatMap: (f: (value: T) => Optional) => f(value) as any, 47 | }; 48 | } 49 | function Optional(value: T): Some; 50 | function Optional(value: null): None; 51 | function Optional(value: T | null) { 52 | if (value === null) { 53 | return None; 54 | } 55 | return Some(value); 56 | } 57 | 58 | Optional(3).isEmpty(); // false 59 | Optional(null).isEmpty(); // true 60 | Optional(3).get(); // 3 61 | 62 | type UserType = { 63 | id: number; 64 | name: Optional; 65 | friends: ReadonlyArray; 66 | }; 67 | const userA: UserType = { 68 | id: 2, 69 | name: Optional("Alex"), 70 | friends: [ 71 | { 72 | id: 3, 73 | name: Optional("Mike"), 74 | friends: [ 75 | { 76 | id: 4, 77 | name: Optional("Georgie"), 78 | friends: [], 79 | }, 80 | ], 81 | }, 82 | ], 83 | }; 84 | 85 | const getFriends = (user: UserType): ReadonlyArray => user.friends; 86 | const getName = (user: UserType): Optional => user.name; 87 | const friendsOfFriends = flatten(getFriends(userA).map(getFriends)); 88 | 89 | const userName = Optional(userA).flatMap(getName); 90 | console.log(userName.isEmpty()); // false 91 | console.log((userName as Some).get()); 92 | const evens = (n: number): O.Option => 93 | n % 2 === 0 ? O.none : O.some(n); 94 | -------------------------------------------------------------------------------- /chapters/chapter-6_Functional_Programming_Concepts/Monocle.ts: -------------------------------------------------------------------------------- 1 | import { pipe } from "fp-ts/lib/function"; 2 | import { Lens } from "monocle-ts"; 3 | import { modify, prop } from "monocle-ts/lib/Lens"; 4 | 5 | interface TodoItem { 6 | id: string; 7 | title: string; 8 | completed: boolean; 9 | } 10 | interface TodoListState { 11 | allItemIds: string[]; 12 | byItemId: { [prop: string]: TodoItem }; 13 | } 14 | 15 | const byItemId = Lens.fromProp()("byItemId"); 16 | interface UpdateTodoItemCompletedAction { 17 | type: "UPDATE_TODO_ITEM_COMPLETED"; 18 | id: string; 19 | completed: boolean; 20 | } 21 | function reduceState( 22 | currentState: TodoListState, 23 | action: UpdateTodoItemCompletedAction 24 | ): TodoListState { 25 | switch (action.type) { 26 | case "UPDATE_TODO_ITEM_COMPLETED": 27 | return pipe( 28 | byItemId, 29 | prop(action.id), 30 | prop("completed"), 31 | modify((completed: boolean) => action.completed) 32 | )(currentState); 33 | } 34 | } 35 | const currentState: TodoListState = { 36 | allItemIds: ["1"], 37 | byItemId: { 38 | "1": { 39 | id: "1", 40 | title: "Buy milk", 41 | completed: false, 42 | }, 43 | }, 44 | }; 45 | console.log( 46 | reduceState(currentState, { 47 | type: "UPDATE_TODO_ITEM_COMPLETED", 48 | id: "1", 49 | completed: true, 50 | }) 51 | ); 52 | -------------------------------------------------------------------------------- /chapters/chapter-6_Functional_Programming_Concepts/PureFunctions.ts: -------------------------------------------------------------------------------- 1 | function add1(num: number): number { 2 | return num + 1; 3 | } 4 | 5 | console.log(add1(1)); // 2 6 | console.log(add1(1)); // 2 7 | 8 | function printNumber(num: number): void { 9 | console.log(num); 10 | } 11 | 12 | function toZero(num: number): 0 { 13 | return 0; 14 | } 15 | 16 | export interface IO
{ 17 | (): A; 18 | } 19 | 20 | export const log = 21 | (s: unknown): IO => 22 | () => 23 | console.log(s); 24 | const now: IO = () => new Date().toISOString(); 25 | 26 | log("Hello")(); 27 | console.log(now()); 28 | -------------------------------------------------------------------------------- /chapters/chapter-6_Functional_Programming_Concepts/Recursion.ts: -------------------------------------------------------------------------------- 1 | function factorial(num: number): number { 2 | if (num < 0) return -1; 3 | else if (num == 0) return 1; 4 | else { 5 | return num * factorial(num - 1); 6 | } 7 | } 8 | 9 | function sum(numbers: number[]): number { 10 | function recur(numbers: number[], currentSum: number): number { 11 | if (numbers.length === 0) { 12 | return currentSum; 13 | } 14 | let [first, ...rest] = numbers; 15 | return recur(rest, currentSum + first); 16 | } 17 | 18 | return recur(numbers, 0); 19 | } 20 | 21 | console.log(sum([1, 2, 3, 4, 5])); // 15 22 | 23 | function factorialTail(num: number, result: number = 1): number { 24 | if (num === 0) { 25 | return result; 26 | } 27 | return factorialTail(num - 1, num * result); 28 | } 29 | -------------------------------------------------------------------------------- /chapters/chapter-6_Functional_Programming_Concepts/Referential.ts: -------------------------------------------------------------------------------- 1 | import { IO, log } from "./PureFunctions"; 2 | import * as R from "ramda"; 3 | 4 | function main(): IO { 5 | return R.compose(log, sumList, getArgs)(11, 4); 6 | } 7 | 8 | function sumList(number: number[]): number { 9 | return number.reduce((prev, curr) => prev + curr, 0); 10 | } 11 | 12 | function getArgs(a: number, b: number): number[] { 13 | return [a, b]; 14 | } 15 | 16 | console.log(main()()); // 15 17 | -------------------------------------------------------------------------------- /chapters/chapter-6_Functional_Programming_Concepts/Transducer.ts: -------------------------------------------------------------------------------- 1 | import * as R from "ramda"; 2 | 3 | const collection = [1, 2, 3, 4, 5]; 4 | 5 | function addReducer(curr: number, acc: number): number { 6 | return curr + acc; 7 | } 8 | 9 | console.log(collection.reduce(addReducer, 0)); 10 | console.log(collection.filter((n: number) => (n & 1) === 1).reduce(addReducer)); 11 | 12 | type Transducer = (reducerFn: Reducer) => Reducer; 13 | type Reducer = () => T | ((acc: T) => T) | ((acc: T, curr: K) => T); 14 | 15 | const transducer = R.compose(R.map(R.add(1))); 16 | const result = R.transduce( 17 | transducer, 18 | R.flip(R.append), 19 | [], 20 | collection 21 | ); 22 | console.log(result); // [2, 3, 4, 5, 6] 23 | -------------------------------------------------------------------------------- /chapters/chapter-6_Functional_Programming_Concepts/tempCodeRunnerFile.ts: -------------------------------------------------------------------------------- 1 | function addReducer(curr: number, acc: number): number { 2 | // return curr + acc; 3 | // } 4 | 5 | // console.log(collection.reduce(addReducer, 0)); 6 | // console.log(collection.filter((n: number) => (n & 1) === 1).reduce(addReducer)); 7 | 8 | // type Transducer = (reducerFn: Reducer) => Reducer; 9 | // type Reducer = () => T | ((acc: T) => T) | ((acc: T, curr: K) => T); 10 | 11 | // export type AnyFunction = (...xs: any[]) => T; 12 | -------------------------------------------------------------------------------- /chapters/chapter-7_Reactive_Programming_concepts/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PacktPublishing/TypeScript-4-Design-Patterns-and-Best-Practices/794888bd4c351b9565e01828440e3d0cf06d26c6/chapters/chapter-7_Reactive_Programming_concepts/.gitkeep -------------------------------------------------------------------------------- /chapters/chapter-7_Reactive_Programming_concepts/Futures.ts: -------------------------------------------------------------------------------- 1 | // resolve(): Future; 2 | // resolve(value: T | FutureLike): Future; 3 | // interface FutureLike { 4 | // then( 5 | // onfulfilled?: 6 | // | ((value: T) => TResult1 | FutureLike) 7 | // | undefined 8 | // | null, 9 | // onrejected?: 10 | // | ((reason: any) => TResult2 | FutureLike) 11 | // | undefined 12 | // | null 13 | // ): FutureLike; 14 | // } 15 | 16 | // interface FutureConstructor { 17 | // readonly prototype: Future; 18 | 19 | // new ( 20 | // executor: ( 21 | // resolve: (value: T | FutureLike) => void, 22 | // reject: (reason?: any) => void 23 | // ) => Cancel 24 | // ): Future; 25 | // reject(reason?: any): Future; 26 | // resolve(): Future; 27 | // resolve(value: T | FutureLike): Future; 28 | // } 29 | 30 | // declare var Future: FutureConstructor; 31 | 32 | // interface Future { 33 | // then( 34 | // onfulfilled?: 35 | // | ((value: T) => TResult1 | FutureLike) 36 | // | undefined 37 | // | null, 38 | // onrejected?: 39 | // | ((reason: any) => TResult2 | FutureLike) 40 | // | undefined 41 | // | null 42 | // ): Future; 43 | // catch( 44 | // onrejected?: 45 | // | ((reason: any) => TResult | FutureLike) 46 | // | undefined 47 | // | null 48 | // ): Future; 49 | // } 50 | 51 | import { noop } from "lodash"; 52 | export type Reject = (reason?: any) => void; 53 | export type Resolve = (t: TResult) => void; 54 | 55 | export type Execution = ( 56 | resolve: (value: T) => void, 57 | reject: (reason?: any) => void 58 | ) => () => void; 59 | 60 | class Future { 61 | private fn: Execution; 62 | 63 | constructor(ex: Execution) { 64 | this.fn = ex; 65 | } 66 | fork(reject: Reject, resolve: Resolve): () => void { 67 | return this.fn(reject, resolve); 68 | } 69 | 70 | resolve(): Promise { 71 | return new Promise((resolve, reject) => { 72 | this.fn(reject, resolve); 73 | }); 74 | } 75 | 76 | static success(t: T): Future { 77 | return new Future((reject: Reject, resolve: Resolve) => { 78 | resolve(t); 79 | return noop; 80 | }); 81 | } 82 | 83 | static fail(err: E): Future { 84 | return new Future((reject: Reject, resolve: Resolve) => { 85 | reject(err); 86 | return noop; 87 | }); 88 | } 89 | then(f: (t: T) => Future): Future { 90 | return new Future((reject: Reject, resolve: Resolve) => { 91 | return this.fn( 92 | (err) => reject(err), 93 | (a: T) => f(a).fork(reject, resolve) 94 | ); 95 | }); 96 | } 97 | } 98 | 99 | const task = new Future((reject, resolve: Resolve) => { 100 | const v = setTimeout(() => resolve("Result"), 1000); 101 | return () => clearTimeout(v); 102 | }).then((value: string) => { 103 | return Future.success(value.toUpperCase()); 104 | }); 105 | const cancelTask = task.fork( 106 | (err) => console.error(err), 107 | (result) => console.warn(`Task Success: ${result}`) 108 | ); 109 | -------------------------------------------------------------------------------- /chapters/chapter-7_Reactive_Programming_concepts/Observables.ts: -------------------------------------------------------------------------------- 1 | import { of, from, Observable, interval } from "rxjs"; 2 | import { take, share } from "rxjs/operators"; 3 | 4 | // From constant values or objects 5 | of(1, 2, 3, 4, 5); 6 | of({ id: 1, data: "value" }); 7 | 8 | // From array of values 9 | from([1, 2, 3, 4, 5]); 10 | 11 | // From a Promise 12 | from(Promise.resolve("users data")); 13 | 14 | function* getNextRandom() { 15 | yield Math.random() * 100; 16 | } 17 | 18 | // From a custom producer function 19 | const randomValues = new Observable((subscriber) => { 20 | subscriber.next(1); 21 | subscriber.next(2); 22 | subscriber.next(3); 23 | setInterval(() => { 24 | subscriber.next(getNextRandom().next().value); 25 | }, 1000); 26 | }); 27 | 28 | // randomValues.subscribe((value) => { 29 | // console.log("Value received: ", value); 30 | // }, undefined); 31 | 32 | let origin = from([1, 2, 3, 4, new Error("Error"), 6]); 33 | origin.subscribe( 34 | (v: any) => { 35 | console.log("Value accepted: ", v); 36 | }, 37 | (e) => { 38 | console.log("Error accepted: ", e); 39 | }, 40 | () => { 41 | console.log("Finished"); 42 | } 43 | ); 44 | 45 | setTimeout(() => { 46 | origin.subscribe( 47 | (v: number | Error) => { 48 | console.log("Value accepted: ", v); 49 | }, 50 | (e) => { 51 | console.log("Error accepted: ", e); 52 | }, 53 | () => { 54 | console.log("Finished"); 55 | } 56 | ); 57 | }, 1000); 58 | 59 | const stream$ = interval(1000).pipe(take(5), share()); 60 | stream$.subscribe((v) => 61 | console.log("Value accepted from first subscriber: ", v) 62 | ); 63 | 64 | setTimeout(() => { 65 | stream$.subscribe((v) => { 66 | console.log("Value accepted from second subscriber: ", v); 67 | }); 68 | }, 2000); 69 | -------------------------------------------------------------------------------- /chapters/chapter-7_Reactive_Programming_concepts/Operators.ts: -------------------------------------------------------------------------------- 1 | import { of, from } from "rxjs"; 2 | import { filter, map, pluck, mapTo } from "rxjs/operators"; 3 | // map 4 | of(1, 2, 3, 4, 5) 5 | .pipe(map((v: number) => v * 2)) 6 | .subscribe((v: number) => console.log(`Value transformed: ${v}`)); 7 | // filter 8 | of(1, 2, 3, 4, 5) 9 | .pipe(filter((v: number) => v % 2 == 0)) 10 | .subscribe((v: number) => console.log(`Value transformed: ${v}`)); 11 | // pluck 12 | from([ 13 | { 14 | id: 1, 15 | name: "Theo", 16 | }, 17 | { 18 | id: 2, 19 | name: "Alex", 20 | }, 21 | ]) 22 | .pipe(pluck("name")) 23 | .subscribe((v: string) => console.log(`Value plucked: ${v}`)); 24 | 25 | from([ 26 | { 27 | id: 1, 28 | name: "Theo", 29 | }, 30 | { 31 | id: 2, 32 | name: "Alex", 33 | }, 34 | ]) 35 | .pipe(map((v) => v.name)) 36 | .subscribe((v: string) => console.log(`Value plucked: ${v}`)); 37 | -------------------------------------------------------------------------------- /chapters/chapter-7_Reactive_Programming_concepts/Patterns.ts: -------------------------------------------------------------------------------- 1 | import { 2 | Subject, 3 | Subscriber, 4 | } from "../chapter-5_Behavioral_Design_Patterns/Observer"; 5 | 6 | export interface AsyncRequest { 7 | success: boolean; 8 | data?: T; 9 | } 10 | 11 | export async function asyncPoll( 12 | fn: () => PromiseLike>, 13 | pollInterval = 5 * 1000, 14 | pollTimeout = 30 * 1000 15 | ): Promise { 16 | const endTime = new Date().getTime() + pollTimeout; 17 | const checkCondition = (resolve: Function, reject: Function): void => { 18 | Promise.resolve(fn()) 19 | .then((result) => { 20 | const now = new Date().getTime(); 21 | if (result.success) { 22 | resolve(result.data); 23 | } else if (now < endTime) { 24 | setTimeout(checkCondition, pollInterval, resolve, reject); 25 | } else { 26 | reject(new Error("Reached timeout. Exiting")); 27 | } 28 | }) 29 | .catch((err) => { 30 | reject(err); 31 | }); 32 | }; 33 | return new Promise(checkCondition); 34 | } 35 | 36 | const result = asyncPoll(async () => { 37 | try { 38 | const result = await Promise.resolve({ data: "Value" }); 39 | if (result.data) { 40 | return Promise.resolve({ 41 | success: true, 42 | data: result, 43 | }); 44 | } else { 45 | return Promise.resolve({ 46 | success: false, 47 | }); 48 | } 49 | } catch (err) { 50 | return Promise.reject(err); 51 | } 52 | }); 53 | 54 | result.then((d) => { 55 | console.log(d); 56 | }); 57 | 58 | const source = [1, 3, 4]; 59 | // const iter = new ListIterator(source); 60 | 61 | // function pollOnData(iterator: ListIterator) { 62 | // while (iterator.hasNext()) { 63 | // console.log("Processing data:", iterator.next()); 64 | // } 65 | // } 66 | // // producer 67 | // setTimeout(() => { 68 | // source.push(Math.floor(Math.random() * 100)); 69 | // }, 1000); 70 | 71 | // // consumer 72 | // setTimeout(() => { 73 | // pollOnData(iter); 74 | // }, 2000); 75 | 76 | export class Producer extends Subject { 77 | constructor(private state: number[]) { 78 | super(); 79 | this.state = state; 80 | } 81 | 82 | getState(): number[] { 83 | return this.state; 84 | } 85 | 86 | setState(state: number[]) { 87 | this.state = state; 88 | } 89 | } 90 | 91 | export class Consumer implements Subscriber { 92 | private state: any; 93 | 94 | constructor(private subject: Producer) {} 95 | 96 | public notify(): void { 97 | this.state = this.subject.getState(); 98 | for (let item of this.state) { 99 | console.log("Processing data:", item); 100 | } 101 | } 102 | } 103 | 104 | const producer = new Producer([]); 105 | const subA = new Consumer(producer); 106 | producer.addSubscriber(subA); 107 | 108 | const subB = new Consumer(producer); 109 | producer.addSubscriber(subB); 110 | 111 | setInterval(() => { 112 | producer.setState( 113 | producer.getState().concat(Math.floor(Math.random() * 100)) 114 | ); 115 | producer.notify(); 116 | }, 1000); 117 | -------------------------------------------------------------------------------- /chapters/chapter-7_Reactive_Programming_concepts/Promises.ts: -------------------------------------------------------------------------------- 1 | const fetcher = require("node-fetch"); 2 | 3 | const pullFromApi = fetcher( 4 | "https://jsonplaceholder.typicode.com/todos/1" 5 | ).then((response) => response.json()); 6 | 7 | pullFromApi.then((result) => { 8 | console.log(result); 9 | }); 10 | 11 | const promiseValue = Promise.resolve("Value"); 12 | const promiseError = Promise.reject("Value"); 13 | function delay(ms: number = 1000) { 14 | return new Promise((resolve) => setTimeout(resolve, ms)); 15 | } 16 | function failAfter(ms: number = 1000) { 17 | return new Promise((_, reject) => setTimeout(reject, ms)); 18 | } 19 | const races = Promise.race([delay(1000), failAfter(500)]); 20 | const all = Promise.all([delay(1000), failAfter(1500)]); 21 | const settled = Promise.allSettled([delay(1000), failAfter(500)]); 22 | (async () => { 23 | races 24 | .then((value) => { 25 | console.log(value); 26 | }) 27 | .catch((_) => { 28 | console.log("Error"); 29 | }); 30 | })(); 31 | (async () => { 32 | all 33 | .then((value) => { 34 | console.log(value); 35 | }) 36 | .catch((_) => { 37 | console.log("Error"); 38 | }); 39 | })(); 40 | (async () => { 41 | settled 42 | .then((value) => { 43 | console.log(value); 44 | }) 45 | .catch((_) => { 46 | console.log("Error"); 47 | }); 48 | })(); 49 | -------------------------------------------------------------------------------- /chapters/chapter-7_Reactive_Programming_concepts/example.ts: -------------------------------------------------------------------------------- 1 | { 2 | class ChristmasBox { 3 | private constructor(private value: TSurprise) { 4 | this.value = value; 5 | } 6 | static with(value: any): ChristmasBox { 7 | return new ChristmasBox(value); 8 | } 9 | 10 | open(): TSurprise { 11 | return this.value; 12 | } 13 | 14 | turnInto( 15 | f: (v: TSurprise) => TwistedSurprise 16 | ): ChristmasBox { 17 | return ChristmasBox.with(f(this.value)); 18 | } 19 | 20 | chain( 21 | fn: (v: TSurprise) => ChristmasBox 22 | ): ChristmasBox { 23 | if (this.value instanceof ChristmasBox) { 24 | return fn(this.value.open()); 25 | } else { 26 | return fn(this.value); 27 | } 28 | } 29 | } 30 | 31 | console.log(ChristmasBox.with("Chocolate candy")); // Oops we got a close box. We want it open 32 | console.log(ChristmasBox.with("Chocolate candy").open()); // Thats better! 33 | 34 | const molases = (candy: string) => ChristmasBox.with("Molasses"); 35 | const ourPresent = ChristmasBox.with("Chocolate candy"); 36 | ourPresent.turnInto(molases); 37 | const ourNewPresent = ourPresent.turnInto(molases); 38 | 39 | console.log(ourNewPresent.open()); // Yikes! 40 | 41 | const chocolateFountain = (mollases: string) => 42 | ChristmasBox.with("Chocolate fountain"); 43 | 44 | const ourNewNewPresent = ourPresent.chain(molases).chain(chocolateFountain); 45 | 46 | console.log(ourNewNewPresent.open()); // Yum Yum! 47 | } 48 | 49 | type T0 = Extract<"a" | "b" | "c", "a" | "b" | "f">; 50 | 51 | type T1 = Extract void), Function>; 52 | 53 | type T1 = () => void; 54 | -------------------------------------------------------------------------------- /chapters/chapter-8_Best_practices/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PacktPublishing/TypeScript-4-Design-Patterns-and-Best-Practices/794888bd4c351b9565e01828440e3d0cf06d26c6/chapters/chapter-8_Best_practices/.gitkeep -------------------------------------------------------------------------------- /chapters/chapter-8_Best_practices/DDD.ts: -------------------------------------------------------------------------------- 1 | export abstract class Value { 2 | constructor(protected props: T) { 3 | this.props = props; 4 | } 5 | } 6 | 7 | export interface AddressProps { 8 | country: string; 9 | postCode: string; 10 | street: string; 11 | } 12 | 13 | export class Address extends Value { 14 | get country(): string { 15 | return this.props.country; 16 | } 17 | 18 | get postCode(): string { 19 | return this.props.postCode; 20 | } 21 | 22 | get street(): string { 23 | return this.props.street; 24 | } 25 | 26 | equals(other?: Value): boolean { 27 | if (!other) { 28 | return false; 29 | } 30 | return JSON.stringify(this) === JSON.stringify(other); 31 | } 32 | 33 | protected isValid?(props: AddressProps): void { 34 | // Check if address if valid 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /chapters/chapter-8_Best_practices/PatternCombinations.ts: -------------------------------------------------------------------------------- 1 | import Singleton from "chapters/chapter-3_Creational_Design_Patterns/singleton"; 2 | 3 | { 4 | type ServiceA = { 5 | action: () => void; 6 | }; 7 | type ServiceB = { 8 | action: () => void; 9 | }; 10 | 11 | class Facade { 12 | private static instance: Facade; 13 | private constructor( 14 | private serviceA: ServiceA, 15 | private serviceB: ServiceB 16 | ) {} 17 | static getInstance(serviceA: ServiceA, serviceB: ServiceB) { 18 | if (!Facade.instance) { 19 | Facade.instance = new Facade(serviceA, serviceB); 20 | } 21 | return Facade.instance; 22 | } 23 | 24 | perform() { 25 | this.serviceA.action(); 26 | this.serviceB.action(); 27 | // more complex logic here 28 | } 29 | } 30 | 31 | interface Iterator { 32 | next(): T | null; 33 | hasNext(): boolean; 34 | } 35 | 36 | type Component = { 37 | parent: Component; 38 | }; 39 | 40 | class Composite { 41 | private children: Component[] = []; 42 | 43 | getIterator(): Iterator { 44 | return new (class { 45 | private index: number = 0; 46 | constructor(public superThis: Composite) {} 47 | 48 | key(): number { 49 | return this.index; 50 | } 51 | next(): Component { 52 | const item = this.superThis.children[this.index]; 53 | this.index += 1; 54 | return item; 55 | } 56 | 57 | hasNext(): boolean { 58 | return this.index < this.superThis.children.length; 59 | } 60 | })(this); 61 | } 62 | } 63 | } 64 | 65 | { 66 | interface Command { 67 | execute(): void; 68 | } 69 | class SaveCommand implements Command { 70 | constructor(private receiver: Receiver) {} 71 | 72 | execute() { 73 | this.receiver.saveMemento(); 74 | } 75 | } 76 | class RestoreCommand implements Command { 77 | constructor(private receiver: Receiver) {} 78 | 79 | execute() { 80 | this.receiver.restoreLastMemento(); 81 | } 82 | } 83 | interface Receiver { 84 | restoreLastMemento(); 85 | saveMemento(); 86 | } 87 | type Originator = { 88 | save: () => Memento; 89 | restore: (memento: Memento) => void; 90 | }; 91 | type Memento = string; 92 | class CareTaker implements Receiver { 93 | constructor( 94 | private originator: Originator, 95 | private mementos: Memento[] = [] 96 | ) {} 97 | 98 | restoreLastMemento() { 99 | if (this.mementos.length === 0) { 100 | return; 101 | } 102 | const memento = this.mementos.pop()!; 103 | this.originator.restore(memento); 104 | } 105 | 106 | saveMemento() { 107 | this.mementos.push(this.originator.save()); 108 | } 109 | } 110 | } 111 | -------------------------------------------------------------------------------- /chapters/chapter-8_Best_practices/SOLID.ts: -------------------------------------------------------------------------------- 1 | import { Collection } from "immutable"; 2 | import { kebabCase } from "lodash"; 3 | 4 | { 5 | class User { 6 | constructor( 7 | private name: string, 8 | private email: string, 9 | private password: string 10 | ) {} 11 | 12 | generateSlug(): string { 13 | return kebabCase(this.name); 14 | } 15 | 16 | login(email: string, password: string) {} 17 | sendEmail(email: string, template: string) {} 18 | } 19 | 20 | class UserAccountService { 21 | login(user: User, password: string) {} 22 | } 23 | 24 | class EmailService { 25 | sendEmailToUser(user: User, template: string) {} 26 | } 27 | } 28 | 29 | { 30 | type AccountType = "Normal" | "Premium" | "Ultimate"; 31 | class User { 32 | constructor( 33 | private name: string, 34 | private email: string, 35 | private password: string, 36 | private accountType: AccountType = "Normal" 37 | ) {} 38 | isPremium(): boolean { 39 | return this.accountType === "Premium"; 40 | } 41 | getAccountType(): AccountType { 42 | return this.accountType; 43 | } 44 | } 45 | type Voucher = string; 46 | const userTypeToVoucherMap: Record = { 47 | Normal: "10% discount", 48 | Premium: "15% discount", 49 | Ultimate: "20% discount", 50 | }; 51 | class VoucherService { 52 | getVoucher(user: User): string { 53 | return userTypeToVoucherMap[user.getAccountType()]; 54 | } 55 | } 56 | } 57 | 58 | { 59 | interface Bag { 60 | push(item: T); 61 | pop(): T | undefined; 62 | isEmpty(): boolean; 63 | } 64 | 65 | class Queue implements Bag { 66 | constructor(private items = []) {} 67 | push(item: T) {} 68 | pop(): T | undefined { 69 | if (this.items.length > 0) { 70 | return this.items.pop(); 71 | } 72 | return undefined; 73 | } 74 | isEmpty(): boolean { 75 | return this.items.length === 0; 76 | } 77 | } 78 | 79 | class Stack implements Bag { 80 | constructor(private items: T[] = []) {} 81 | push(item: T) {} 82 | pop(): T | undefined { 83 | if (this.items.length > 0) { 84 | return this.items.pop(); 85 | } 86 | return undefined; 87 | } 88 | isEmpty(): boolean { 89 | return this.items.length === 0; 90 | } 91 | } 92 | 93 | class NonEmptyStack implements Bag { 94 | private tag: any = Symbol(); 95 | constructor(private items: T[] = []) { 96 | if (this.items.length == 0) { 97 | this.items.push(this.tag); 98 | } 99 | } 100 | 101 | push(item: T) { 102 | this.items.push(item); 103 | } 104 | pop(): T | undefined { 105 | if (this.items.length === 1) { 106 | const item = this.items.pop(); 107 | this.items.push(this.tag); 108 | return item; 109 | } 110 | if (this.items.length > 1) { 111 | return this.items.pop(); 112 | } 113 | return undefined; 114 | } 115 | isEmpty(): boolean { 116 | return this.items.length === 0; 117 | } 118 | } 119 | 120 | class Link { 121 | constructor(private start: string, end: string) {} 122 | } 123 | 124 | function visit(bag: Bag) { 125 | bag.push(new Link("1", "2")); 126 | while (!bag.isEmpty()) { 127 | const r = bag.pop(); 128 | } 129 | } 130 | } 131 | 132 | { 133 | type User = { 134 | id: string; 135 | name: string; 136 | email: string; 137 | }; 138 | interface UserService { 139 | findBy(id: string): User | undefined; 140 | create(specification: User): void; 141 | update(id: string, values: Partial): void; 142 | } 143 | 144 | interface Collection { 145 | pushBack(item: T): void; 146 | popBack(): T; 147 | pushFront(item: T): void; 148 | popFront(): T; 149 | isEmpty(): boolean; 150 | insertAt(item: T, index: number): void; 151 | deleteAt(index: number): T | undefined; 152 | } 153 | 154 | interface Collection { 155 | isEmpty(): boolean; 156 | } 157 | 158 | interface Array extends Collection { 159 | insertAt(item: T, index: number): void; 160 | deleteAt(index: number): T | undefined; 161 | } 162 | 163 | interface Stack extends Collection { 164 | pushFront(item: T): void; 165 | popFront(): T; 166 | } 167 | 168 | interface Queue extends Collection { 169 | pushBack(item: T): void; 170 | popFront(): T; 171 | } 172 | } 173 | 174 | { 175 | type User = { 176 | name: string; 177 | email: string; 178 | }; 179 | class UserService { 180 | constructor() {} 181 | findByEmail(email: string): User | undefined { 182 | const userRepo = UserRepositoryFactory.getInstance(); 183 | return userRepo.findByEmail(email); 184 | } 185 | } 186 | class UserRepositoryFactory { 187 | static getInstance(): UserRepository { 188 | return new UserRepository(); 189 | } 190 | } 191 | 192 | class UserRepository { 193 | users: User[] = [{ name: "Theo", email: "theo@example.com" }]; 194 | findByEmail(email: string): User | undefined { 195 | const user = this.users.find((u) => u.email === email); 196 | return user; 197 | } 198 | } 199 | } 200 | 201 | { 202 | type User = { 203 | name: string; 204 | email: string; 205 | }; 206 | interface UserQuery { 207 | findByEmail(email: string): User | undefined; 208 | } 209 | class UserService { 210 | constructor( 211 | private userQuery: UserQuery = UserRepositoryFactory.getInstance() 212 | ) {} 213 | findByEmail(email: string): User | undefined { 214 | return this.userQuery.findByEmail(email); 215 | } 216 | } 217 | class UserRepositoryFactory { 218 | static getInstance(): UserRepository { 219 | return new UserRepository(); 220 | } 221 | } 222 | 223 | class UserRepository implements UserQuery { 224 | users: User[] = [{ name: "Theo", email: "theo@example.com" }]; 225 | findByEmail(email: string): User | undefined { 226 | const user = this.users.find((u) => u.email === email); 227 | return user; 228 | } 229 | } 230 | } 231 | -------------------------------------------------------------------------------- /chapters/chapter-8_Best_practices/Utilities.ts: -------------------------------------------------------------------------------- 1 | { 2 | type Address = Readonly<{ 3 | streetNum: number; 4 | town: string; 5 | streetName: string; 6 | }>; 7 | type User = Readonly<{ 8 | name: string; 9 | email: string; 10 | address: Address; 11 | }>; 12 | 13 | function loginUser(user: User) { 14 | // perform login 15 | } 16 | } 17 | 18 | type Address = { 19 | streetNum: number; 20 | town: string; 21 | streetName: string; 22 | }; 23 | type User = { 24 | name: string; 25 | email: string; 26 | address: Address; 27 | }; 28 | 29 | export type UserRead = Readonly; 30 | export type UserWrite = User; 31 | 32 | { 33 | type UserKind = "normal" | "visitor" | "admin"; 34 | type PartialRecord = { 35 | [P in K]?: T; 36 | }; 37 | type map = PartialRecord; 38 | const errorMessageForUserViewRequestMap: PartialRecord = { 39 | normal: "We're having trouble Please try again in a few minutes.", 40 | 41 | visitor: "You do not have permissions to view this page.", 42 | }; 43 | } 44 | 45 | console.log(Object.getOwnPropertyNames(String.prototype)); 46 | -------------------------------------------------------------------------------- /chapters/chapter-9_Anti_patterns/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PacktPublishing/TypeScript-4-Design-Patterns-and-Best-Practices/794888bd4c351b9565e01828440e3d0cf06d26c6/chapters/chapter-9_Anti_patterns/.gitkeep -------------------------------------------------------------------------------- /chapters/chapter-9_Anti_patterns/ClassesFatigue.ts: -------------------------------------------------------------------------------- 1 | interface Shape {} 2 | class Circle implements Shape {} 3 | class Rectangle implements Shape {} 4 | class Polygon implements Shape {} 5 | class Square extends Polygon {} 6 | class Triangle extends Polygon {} 7 | 8 | interface Reader { 9 | read(): string[]; 10 | } 11 | 12 | interface Writer { 13 | write(input: string[]): void; 14 | } 15 | 16 | class CSV implements Reader, Writer { 17 | constructor(csvFilePath: string) {} 18 | 19 | read(): string[] { 20 | return []; 21 | } 22 | write(input: string[]): void {} 23 | } 24 | { 25 | class CSVReader implements Reader { 26 | constructor(csvFilePath: string) {} 27 | 28 | read(): string[] { 29 | return []; 30 | } 31 | } 32 | 33 | class CSVWriter implements Writer { 34 | constructor() {} 35 | 36 | write(input: string[]): void {} 37 | } 38 | class ExcelReader implements Reader { 39 | constructor(excelFilePath: string) {} 40 | 41 | read(): string[] { 42 | return []; 43 | } 44 | } 45 | class PDFWriter implements Writer { 46 | constructor(pdfFilePath: string) {} 47 | 48 | write(input: string[]): void {} 49 | } 50 | 51 | class ReaderToWriters { 52 | constructor(private reader: Reader, private writers: Writer[]) {} 53 | perform() { 54 | const lines = this.reader.read(); 55 | this.writers.forEach((w) => w.write(lines)); 56 | } 57 | } 58 | } 59 | class ExcelToCSV extends CSV { 60 | constructor(csvFilePath: string, excelFilePath: string) { 61 | super(csvFilePath); 62 | } 63 | 64 | read(): string[] { 65 | // Read from Excel file 66 | return []; 67 | } 68 | } 69 | class ExcelToPDF extends ExcelToCSV { 70 | constructor(csvFilePath: string, excelFilePath: string, pdfFilePath: string) { 71 | super("", excelFilePath); 72 | } 73 | 74 | write(): string[] { 75 | // Write to PDF 76 | return []; 77 | } 78 | } 79 | 80 | interface Animal { 81 | eat(): void; 82 | } 83 | interface Mammal extends Animal { 84 | sleep(): void; 85 | } 86 | interface Flyer extends Animal { 87 | fly(): void; 88 | } 89 | class Bat implements Mammal, Flyer { 90 | fly(): void {} 91 | eat(): void {} 92 | sleep(): void {} 93 | } 94 | 95 | // interface Configuration { 96 | // paths: { 97 | // apiBase: string; 98 | // login: string; 99 | // }; 100 | // } 101 | 102 | const applicationConfig = { 103 | paths: { 104 | apiBase: "/v1", 105 | login: "/login", 106 | }, 107 | flags: { 108 | isEnabled: true, 109 | isAvailable() { 110 | return this.isEnabled; 111 | }, 112 | }, 113 | }; 114 | 115 | type Configuration = Readonly; 116 | -------------------------------------------------------------------------------- /chapters/chapter-9_Anti_patterns/LanguageIdioms.ts: -------------------------------------------------------------------------------- 1 | import { fst } from "fp-ts/lib/ReadonlyTuple"; 2 | import { type } from "ramda"; 3 | 4 | class Employee { 5 | private id: string; 6 | private name: string; 7 | constructor(id: string, name: string) { 8 | this.id = id; 9 | this.name = name; 10 | } 11 | getName(): string { 12 | return this.name; 13 | } 14 | 15 | setName(name: string) { 16 | this.name = name; 17 | } 18 | 19 | getId(): string { 20 | return this.id; 21 | } 22 | } 23 | console.log(JSON.stringify(new Employee("Theo", "1"))); // {"id":"Theo","name":"1"} 24 | 25 | { 26 | type Employee = Readonly<{ 27 | name: string; 28 | id: string; 29 | }>; 30 | 31 | const theo: Employee = { 32 | name: "Theo", 33 | id: "1", 34 | }; 35 | 36 | const newTheo: Employee = { ...theo, id: "2" }; 37 | } 38 | 39 | interface error { 40 | Error(): string; 41 | } 42 | 43 | import * as fs from "fs"; 44 | import * as fsPromises from "fs/promises"; 45 | fs.readFile("example.txt", function (err, data) { 46 | if (err) { 47 | return console.error(err); 48 | } 49 | console.log("File contents" + data.toString()); 50 | }); 51 | 52 | async function readFist1024bytes() { 53 | let file: fsPromises.FileHandle | null = null; 54 | const buffer = Buffer.alloc(1024); 55 | try { 56 | file = await fsPromises.open("example.txt", "r+"); 57 | await file.read(buffer); 58 | } finally { 59 | if (file) { 60 | await file.close(); 61 | } 62 | } 63 | console.log(buffer.toString()); 64 | } 65 | 66 | // do something with the open *File f 67 | readFist1024bytes(); 68 | -------------------------------------------------------------------------------- /chapters/chapter-9_Anti_patterns/PermissiveTypes.ts: -------------------------------------------------------------------------------- 1 | interface Callback { 2 | onEvent: Function; 3 | } 4 | 5 | const callBack1: Callback = { 6 | onEvent: (a: string) => a, 7 | }; 8 | 9 | const callBack2: Callback = { 10 | onEvent: () => "a", 11 | }; 12 | 13 | const callBack3: Callback = { 14 | onEvent: () => 1, 15 | }; 16 | -------------------------------------------------------------------------------- /chapters/chapter-9_Anti_patterns/RuntimeAssertions.ts: -------------------------------------------------------------------------------- 1 | function divMod(x: number, y: number): [number, number] { 2 | assertIsNumber(x); 3 | assertIsNumber(y); 4 | assertNotZero(y); 5 | return [Math.floor(x / y), x % y]; 6 | } 7 | 8 | export function assertIsNumber( 9 | val: T, 10 | errorMessage = `Expected 'val' to be a number, but received: '${val}'` 11 | ) { 12 | if (typeof val !== "number") { 13 | throw new Error(errorMessage); 14 | } 15 | } 16 | 17 | export function assertNotZero( 18 | val: T, 19 | errorMessage = `Expected 'val' to be a non zero number, but received: '${val}'` 20 | ) { 21 | if (val === 0) { 22 | throw new Error(errorMessage); 23 | } 24 | } 25 | 26 | // divMod(2, 0); 27 | -------------------------------------------------------------------------------- /chapters/chapter-9_Anti_patterns/TypeInference.ts: -------------------------------------------------------------------------------- 1 | function example(param: { value: "a" }) { 2 | return param; 3 | } 4 | 5 | const myParam = { 6 | value: "a", 7 | }; 8 | 9 | example(myParam); 10 | 11 | // Literal types are widened when they are inferred for mutable locations (such as the value property in your example) unless you have a type annotation or type assertion indicating otherwise. 12 | 13 | const boo = function () { 14 | return 1; 15 | }; 16 | -------------------------------------------------------------------------------- /chapters/chapter-9_Anti_patterns/inference.ts: -------------------------------------------------------------------------------- 1 | import { values } from "ramda"; 2 | 3 | const arr = [1, 2, 3]; 4 | let x; 5 | x = 2; 6 | 7 | function example(param: { value: string } = { value: "a" }) { 8 | return param; 9 | } 10 | 11 | const param = { 12 | value: "a", 13 | }; 14 | example(param); 15 | 16 | import fetch from "node-fetch"; 17 | 18 | fetch("https://jsonplaceholder.typicode.com/users/1") 19 | .then(async (res) => { 20 | return mapUser(await res.json()); 21 | }) 22 | .then((res) => { 23 | console.log(res); 24 | }); 25 | 26 | type UserResponse = { 27 | id: string; 28 | name: string; 29 | username: string; 30 | }; 31 | 32 | function mapJsonResponse(json: any): UserResponse[] { 33 | const result: UserResponse[] = []; 34 | for (let item of json) { 35 | result.push({ 36 | id: item["id"] ?? null, 37 | name: item["name"] ?? null, 38 | username: item["username"] ?? null, 39 | }); 40 | } 41 | return result; 42 | } 43 | 44 | function mapUser(user: any) { 45 | return { 46 | id: user["id"] ?? null, 47 | name: user["name"] ?? null, 48 | username: user["username"] ?? null, 49 | }; 50 | } 51 | -------------------------------------------------------------------------------- /chapters/smoke.ts: -------------------------------------------------------------------------------- 1 | export function sum(a: number, b: number) { 2 | return a + b; 3 | } 4 | 5 | export function forOf(arr: Array, cb: (item: T) => void) { 6 | for (let item of arr) { 7 | cb(item) 8 | } 9 | } 10 | 11 | type OneOrTwo = 1 | 2 12 | forOf([1,2,3,"4","Hello", 1 as OneOrTwo], (item) => console.info(item)) -------------------------------------------------------------------------------- /chapters/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "../tsconfig.json", 3 | "compilerOptions": { 4 | // compiler options 5 | "composite": false, 6 | "allowJs": true, 7 | "noImplicitAny": false 8 | } 9 | } -------------------------------------------------------------------------------- /example.txt: -------------------------------------------------------------------------------- 1 | hello 2 | -------------------------------------------------------------------------------- /images/playground_code.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PacktPublishing/TypeScript-4-Design-Patterns-and-Best-Practices/794888bd4c351b9565e01828440e3d0cf06d26c6/images/playground_code.png -------------------------------------------------------------------------------- /jest.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | roots: ['/chapters'], 3 | transform: { 4 | '^.+\\.tsx?$': 'ts-jest', 5 | }, 6 | testRegex: '(/__tests__/.*|(\\.|/)(test|spec))\\.tsx?$', 7 | moduleFileExtensions: ['ts', 'tsx', 'js', 'jsx', 'json', 'node'], 8 | } -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "TypeScript-4-Design-Patterns-and-Best-Practices", 3 | "version": "1.0.0", 4 | "description": "TypeScript 4 Design Patterns and Best Practices Book, Published by Packt", 5 | "main": "index.js", 6 | "dependencies": { 7 | "body-parser": "^1.19.0", 8 | "fp-ts": "^2.9.5", 9 | "immutable": "^4.0.0-rc.12", 10 | "inversify": "^5.0.5", 11 | "inversify-express-utils": "^6.3.2", 12 | "lodash": "^4.17.21", 13 | "monocle-ts": "^2.3.10", 14 | "morgan": "^1.10.0", 15 | "node-fetch": "^2.6.1", 16 | "ramda": "^0.27.1", 17 | "react": "^17.0.1", 18 | "react-dom": "^17.0.1", 19 | "reflect-metadata": "^0.1.13", 20 | "rxjs": "^6.6.7" 21 | }, 22 | "devDependencies": { 23 | "@types/immutable": "^3.8.7", 24 | "@types/jest": "^26.0.20", 25 | "@types/node": "^14.14.25", 26 | "@types/node-fetch": "^2.5.10", 27 | "@types/ramda": "^0.27.40", 28 | "@types/react": "^17.0.2", 29 | "@types/react-dom": "^17.0.1", 30 | "@types/reflect-metadata": "^0.1.0", 31 | "cross-env": "^7.0.3", 32 | "jest": "^26.6.3", 33 | "rimraf": "^3.0.2", 34 | "ts-jest": "^26.5.1", 35 | "ts-loader": "^8.0.17", 36 | "ts-node": "^9.1.1", 37 | "tsconfig-paths": "^3.9.0", 38 | "typescript": "^4.2.2", 39 | "webpack": "^5.24.1", 40 | "webpack-cli": "^4.5.0", 41 | "webpack-dev-server": "^3.11.2" 42 | }, 43 | "scripts": { 44 | "build": "npx tsc --build chapters", 45 | "clean": "npx rimraf dist", 46 | "build:webpack:chapter2": "webpack -c chapters/chapter-2_Core_Principles_and_use_cases/webpack/webpack.config.js", 47 | "serve:webpack:chapter2": "webpack serve --mode development -c chapters/chapter-2_Core_Principles_and_use_cases/webpack/webpack.config.js", 48 | "serve:react:chapter2": "webpack serve --mode development -c chapters/chapter-2_Core_Principles_and_use_cases/react/webpack.config.js", 49 | "rebuild": "npm run clean && npm run build", 50 | "ts": "ts-node", 51 | "test": "jest" 52 | }, 53 | "repository": { 54 | "type": "git", 55 | "url": "git+https://github.com/PacktPublishing/TypeScript-4-Design-Patterns-and-Best-Practices.git" 56 | }, 57 | "keywords": [], 58 | "author": "Theo Despoudis", 59 | "license": "ISC", 60 | "bugs": { 61 | "url": "https://github.com/PacktPublishing/TypeScript-4-Design-Patterns-and-Best-Practices/issues" 62 | }, 63 | "homepage": "https://github.com/PacktPublishing/TypeScript-4-Design-Patterns-and-Best-Practices#readme" 64 | } 65 | -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "baseUrl": "./", 4 | /* Visit https://aka.ms/tsconfig.json to read more about this file */ 5 | 6 | /* Basic Options */ 7 | // "incremental": true, /* Enable incremental compilation */ 8 | "target": "ES2015", /* Specify ECMAScript target version: 'ES3' (default), 'ES5', 'ES2015', 'ES2016', 'ES2017', 'ES2018', 'ES2019', 'ES2020', or 'ESNEXT'. */ 9 | "module": "commonjs", /* Specify module code generation: 'none', 'commonjs', 'amd', 'system', 'umd', 'es2015', 'es2020', or 'ESNext'. */ 10 | "lib": [ 11 | "dom", 12 | "es2015", 13 | "es2020" 14 | ], /* Specify library files to be included in the compilation. */ 15 | // "allowJs": true, /* Allow javascript files to be compiled. */ 16 | // "checkJs": true, /* Report errors in .js files. */ 17 | "jsx": "react", /* Specify JSX code generation: 'preserve', 'react-native', or 'react'. */ 18 | // "declaration": true, /* Generates corresponding '.d.ts' file. */ 19 | // "declarationMap": true, /* Generates a sourcemap for each corresponding '.d.ts' file. */ 20 | "sourceMap": true, /* Generates corresponding '.map' file. */ 21 | // "outFile": "./", /* Concatenate and emit output to single file. */ 22 | "outDir": "./dist", /* Redirect output structure to the directory. */ 23 | // "rootDir": "./", /* Specify the root directory of input files. Use to control the output directory structure with --outDir. */ 24 | // "composite": true, /* Enable project compilation */ 25 | // "tsBuildInfoFile": "./", /* Specify file to store incremental compilation information */ 26 | // "removeComments": true, /* Do not emit comments to output. */ 27 | // "noEmit": true, /* Do not emit outputs. */ 28 | // "importHelpers": true, /* Import emit helpers from 'tslib'. */ 29 | "downlevelIteration": true, /* Provide full support for iterables in 'for-of', spread, and destructuring when targeting 'ES5' or 'ES3'. */ 30 | // "isolatedModules": true, /* Transpile each file as a separate module (similar to 'ts.transpileModule'). */ 31 | 32 | /* Strict Type-Checking Options */ 33 | "strict": true, /* Enable all strict type-checking options. */ 34 | "noImplicitAny": false, /* Raise error on expressions and declarations with an implied 'any' type. */ 35 | "strictNullChecks": true, /* Enable strict null checks. */ 36 | // "strictFunctionTypes": true, /* Enable strict checking of function types. */ 37 | // "strictBindCallApply": true, /* Enable strict 'bind', 'call', and 'apply' methods on functions. */ 38 | // "strictPropertyInitialization": true, /* Enable strict checking of property initialization in classes. */ 39 | // "noImplicitThis": true, /* Raise error on 'this' expressions with an implied 'any' type. */ 40 | // "alwaysStrict": true, /* Parse in strict mode and emit "use strict" for each source file. */ 41 | 42 | /* Additional Checks */ 43 | // "noUnusedLocals": true, /* Report errors on unused locals. */ 44 | // "noUnusedParameters": true, /* Report errors on unused parameters. */ 45 | // "noImplicitReturns": true, /* Report error when not all code paths in function return a value. */ 46 | // "noFallthroughCasesInSwitch": true, /* Report errors for fallthrough cases in switch statement. */ 47 | // "noUncheckedIndexedAccess": true, /* Include 'undefined' in index signature results */ 48 | 49 | /* Module Resolution Options */ 50 | // "moduleResolution": "node", /* Specify module resolution strategy: 'node' (Node.js) or 'classic' (TypeScript pre-1.6). */ 51 | // "baseUrl": "./", /* Base directory to resolve non-absolute module names. */ 52 | // "paths": {}, /* A series of entries which re-map imports to lookup locations relative to the 'baseUrl'. */ 53 | // "rootDirs": [], /* List of root folders whose combined content represents the structure of the project at runtime. */ 54 | // "typeRoots": [], /* List of folders to include type definitions from. */ 55 | // "types": [], /* Type declaration files to be included in compilation. */ 56 | // "allowSyntheticDefaultImports": true, /* Allow default imports from modules with no default export. This does not affect code emit, just typechecking. */ 57 | "esModuleInterop": true, /* Enables emit interoperability between CommonJS and ES Modules via creation of namespace objects for all imports. Implies 'allowSyntheticDefaultImports'. */ 58 | // "preserveSymlinks": true, /* Do not resolve the real path of symlinks. */ 59 | // "allowUmdGlobalAccess": true, /* Allow accessing UMD globals from modules. */ 60 | 61 | /* Source Map Options */ 62 | // "sourceRoot": "", /* Specify the location where debugger should locate TypeScript files instead of source locations. */ 63 | // "mapRoot": "", /* Specify the location where debugger should locate map files instead of generated locations. */ 64 | // "inlineSourceMap": true, /* Emit a single file with source maps instead of having a separate file. */ 65 | // "inlineSources": true, /* Emit the source alongside the sourcemaps within a single file; requires '--inlineSourceMap' or '--sourceMap' to be set. */ 66 | /* Experimental Options */ 67 | "experimentalDecorators": true, /* Enables experimental support for ES7 decorators. */ 68 | "emitDecoratorMetadata": true, /* Enables experimental support for emitting type metadata for decorators. */ 69 | 70 | /* Advanced Options */ 71 | "skipLibCheck": true, /* Skip type checking of declaration files. */ 72 | "forceConsistentCasingInFileNames": true /* Disallow inconsistently-cased references to the same file. */ 73 | }, 74 | "exclude": [ 75 | "node_modules" 76 | ] 77 | } 78 | --------------------------------------------------------------------------------