├── .DS_Store ├── Chapter 01 ├── bugs.ts ├── converted.ts ├── original.js └── tdd.ts ├── Chapter 02 └── javascript-typing.js ├── Chapter 03 ├── aliases.ts ├── any.ts ├── as.ts ├── assertion.ts ├── bigint.ts ├── boolean.ts ├── literal.ts ├── non-null-assertion.ts ├── null.ts ├── number.ts ├── string.ts ├── symbol.ts ├── undefined.ts ├── union.ts └── unknown.ts ├── Chapter 04 ├── my-lib │ ├── my-lib.ts │ └── tsconfig.json ├── old-lib │ ├── src │ │ ├── old-lib.d.ts │ │ └── old-lib.js │ └── tsconfig.json ├── projects │ ├── main-project │ │ ├── src │ │ │ └── main-source-file-1.ts │ │ └── tsconfig.json │ ├── sub-project-1 │ │ ├── src │ │ │ └── sub-source-file-1.ts │ │ └── tsconfig.json │ └── sub-project-2 │ │ ├── src │ │ └── sub-source-file-2.ts │ │ └── tsconfig.json ├── tsconfig.json └── webpack-integration │ ├── .babelrc │ ├── .browserslistrc │ ├── .eslintrc │ ├── .github │ ├── FUNDING.yml │ └── dependabot.yml │ ├── .gitignore │ ├── .stylelintrc.json │ ├── .travis.yml │ ├── LICENSE │ ├── README.md │ ├── package-lock.json │ ├── package.json │ ├── postcss.config.js │ ├── public │ └── .gitkeep │ ├── src │ ├── images │ │ └── webpack.png │ ├── index.html │ ├── scripts │ │ ├── app.ts │ │ └── index.ts │ └── styles │ │ └── index.scss │ ├── tsconfig.json │ └── webpack │ ├── webpack.common.js │ ├── webpack.config.dev.js │ └── webpack.config.prod.js ├── Chapter 05 ├── enums.ts ├── interfaces.ts └── namespaces.ts ├── Chapter 06 ├── arrays.ts ├── objects.ts └── tuples.ts ├── Chapter 07 ├── functions.ts ├── generator-functions.ts └── generic-functions.ts ├── Chapter 08 ├── abstract.ts ├── classes.ts ├── decorators.ts ├── design-patterns.ts ├── generic.ts └── inheritance.ts ├── Chapter 09 └── narrowing.ts ├── Chapter 10 ├── manipulating-types.ts ├── template-literal-types.ts └── utilities.ts ├── Chapter 11 ├── components │ ├── forms │ │ └── search-field.ts │ └── menus │ │ └── main-menu.ts ├── config.json ├── customer-social.ts ├── customer.ts ├── declarations │ ├── external │ │ └── lib1.d.ts │ ├── internal │ │ └── lib2.d.ts │ ├── lib3.ts │ └── lib4.ts ├── lib │ ├── api │ │ ├── api-a.ts │ │ └── index.ts │ ├── index.ts │ ├── my-lib.ts │ └── ui │ │ ├── index.ts │ │ ├── ui-a.ts │ │ └── ui-b.ts ├── module1.ts └── module2.ts ├── Chapter 12 ├── definitely-typed │ └── str │ │ ├── index.d.ts │ │ ├── str-tests.ts │ │ ├── tsconfig.json │ │ └── tslint.json ├── global │ ├── global.html │ ├── my-global-app.ts │ ├── str-global.d.ts │ └── str-global.js ├── module │ ├── my-commonjs-app.ts │ ├── my-module-app.ts │ ├── my-umd-app.ts │ ├── str-commonjs.d.ts │ ├── str-commonjs.js │ ├── str-module.d.ts │ ├── str-module.js │ ├── str-umd.d.ts │ └── str-umd.js └── publish │ ├── package.json │ ├── str.d.ts │ └── str.js ├── LICENSE └── README.md /.DS_Store: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/OrangeAVA/Ultimate-Typescript-Handbook/3345748735cfa2866a88248587fc3ecd689dbf04/.DS_Store -------------------------------------------------------------------------------- /Chapter 01/bugs.ts: -------------------------------------------------------------------------------- 1 | /* +--------------------How TypeScript Prevents Bugs--------------------+ */ 2 | 3 | // const myNumber = 'oops'; 4 | // myNumber.toPrecision(); // error 5 | 6 | // function countProps(obj) { 7 | // return Object.keys(obj).length; 8 | // } 9 | // countProps(); 10 | -------------------------------------------------------------------------------- /Chapter 01/converted.ts: -------------------------------------------------------------------------------- 1 | /* +--------------------How to Start Using TypeScript--------------------+ */ 2 | 3 | // interface PersonAttrs { 4 | // name: string; 5 | // age: number; 6 | // } 7 | 8 | // class Person { 9 | // constructor(attrs: PersonAttrs) { 10 | // for (let prop in attrs) { 11 | // this[prop] = attrs[prop]; 12 | // } 13 | // } 14 | // } 15 | 16 | // const bob = new Person({ 17 | // name: 'Bob', 18 | // age: 44, 19 | // }); 20 | -------------------------------------------------------------------------------- /Chapter 01/original.js: -------------------------------------------------------------------------------- 1 | /* +--------------------How to Start Using TypeScript--------------------+ */ 2 | 3 | // class Person { 4 | // constructor(attrs) { 5 | // for (let prop in attrs) { 6 | // this[prop] = attrs[prop]; 7 | // } 8 | // } 9 | // } 10 | 11 | // const bob = new Person({ 12 | // name: 'Bob', 13 | // age: 44, 14 | // }); 15 | -------------------------------------------------------------------------------- /Chapter 01/tdd.ts: -------------------------------------------------------------------------------- 1 | /* +--------------------Type Driven Development--------------------+ */ 2 | 3 | //declare function createHtmlElement(tagName: string): HTMLElement; 4 | 5 | // implementation 6 | // function createHtmlElement(tagName: string): HTMLElement { 7 | 8 | // } 9 | -------------------------------------------------------------------------------- /Chapter 02/javascript-typing.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | /** 4 | * @type {string} An optional description 5 | */ 6 | // let a = 1; 7 | // a = 'oops'; 8 | 9 | /** 10 | * sum two numbers 11 | * @param {number} num1 12 | * @param {number} num2 13 | * @returns {number} The sum of the parameters 14 | */ 15 | function addNums(num1, num2) { 16 | return num1 + num2; 17 | } 18 | 19 | //addNums(1, null); 20 | -------------------------------------------------------------------------------- /Chapter 03/aliases.ts: -------------------------------------------------------------------------------- 1 | /* +--------------------Type Aliases--------------------+ */ 2 | 3 | // type directions = 'left' | 'right'; 4 | // directions = 'left' | 'right' | 'up' | 'down'; 5 | // type directions2D = directions | 'up' | 'down'; 6 | 7 | // function move2D(direction: directions2D) { 8 | // // move the element 9 | // } 10 | -------------------------------------------------------------------------------- /Chapter 03/any.ts: -------------------------------------------------------------------------------- 1 | /* +--------------------Any--------------------+ */ 2 | 3 | // let anything: any = 'still any'; 4 | // let later; 5 | // later = 'test'; 6 | 7 | // function len(a) { 8 | // return a.length; 9 | // } 10 | // let test = 'a'; 11 | // len(test); // 1 12 | -------------------------------------------------------------------------------- /Chapter 03/as.ts: -------------------------------------------------------------------------------- 1 | /* +--------------------The as Operator--------------------+ */ 2 | 3 | // let anotherVar = 'another' as string; 4 | 5 | /* +--------------------Down-casting--------------------+ */ 6 | 7 | // function clickHandler(event: Event) { 8 | // const childElement = (event.target as HTMLElement).querySelector('#child'); 9 | // } 10 | 11 | /* +--------------------Compound Casting--------------------+ */ 12 | 13 | // const testEvent = { target: document.createElement('div') } as unknown as Event; 14 | // testEvent.banana(); // Property 'banana' does not exist on 'Event' 15 | 16 | // let boolean = 1 as boolean; 17 | 18 | /* +--------------------Old Type-Casting Syntax--------------------+ */ 19 | 20 | // let myNum = 1; 21 | -------------------------------------------------------------------------------- /Chapter 03/assertion.ts: -------------------------------------------------------------------------------- 1 | /* +--------------------Type Assertion--------------------+ */ 2 | 3 | // function splitString(str: string | null) { 4 | // if (str == null) return; 5 | // return str.split(''); 6 | // } 7 | 8 | // splitString(null); 9 | -------------------------------------------------------------------------------- /Chapter 03/bigint.ts: -------------------------------------------------------------------------------- 1 | /* +--------------------BigInt--------------------+ */ 2 | 3 | // let bignum: bigint = BigInt(0); 4 | // const result = bignum * 1; 5 | -------------------------------------------------------------------------------- /Chapter 03/boolean.ts: -------------------------------------------------------------------------------- 1 | /* +--------------------Boolean--------------------+ */ 2 | 3 | // let isEnabled: boolean = true; 4 | // let legacySupport: false = false; 5 | 6 | // let boolean = 1 as boolean; 7 | -------------------------------------------------------------------------------- /Chapter 03/literal.ts: -------------------------------------------------------------------------------- 1 | let literalNum: 1 = 1; 2 | /* +--------------------Literal Types--------------------+ */ 3 | 4 | // let literalString: 'literal'; 5 | // let literalBigInt: 1n = 1n; 6 | 7 | // const inferredLiteral = 1; 8 | 9 | /* +--------------------Literal Union Types--------------------+ */ 10 | 11 | // function move1D(direction: 'left' | 'right') { 12 | // // move the element 13 | // } 14 | 15 | // move1D('up'); // Argument of the type 'up' is not assignable to parameter of type '"left" | "right"'. 16 | -------------------------------------------------------------------------------- /Chapter 03/non-null-assertion.ts: -------------------------------------------------------------------------------- 1 | /* +--------------------Non-null Assertion--------------------+ */ 2 | 3 | // function splitString(str: string | null) { 4 | // return str!.split(''); 5 | // } 6 | -------------------------------------------------------------------------------- /Chapter 03/null.ts: -------------------------------------------------------------------------------- 1 | /* +--------------------Null--------------------+ */ 2 | 3 | // let nothing: null = null; 4 | // later... 5 | // nothing = 1; // Type '1' is not assignable to type 'null' 6 | 7 | // let something: string = 'test'; 8 | // something = null; // error 9 | -------------------------------------------------------------------------------- /Chapter 03/number.ts: -------------------------------------------------------------------------------- 1 | /* +--------------------Number--------------------+ */ 2 | 3 | // let num: number; 4 | 5 | // num = 0; 6 | // if (num === NaN) { 7 | // // ... 8 | // } 9 | -------------------------------------------------------------------------------- /Chapter 03/string.ts: -------------------------------------------------------------------------------- 1 | /* +--------------------String--------------------+ */ 2 | 3 | // let greeting: string = 'Hello'; 4 | -------------------------------------------------------------------------------- /Chapter 03/symbol.ts: -------------------------------------------------------------------------------- 1 | /* +--------------------Symbol--------------------+ */ 2 | 3 | // let aSym: symbol = Symbol(); 4 | // const otherSym: unique symbol = Symbol(); 5 | 6 | // const unique = Symbol(); 7 | // const obj = { 8 | // [unique]: 'unique value', 9 | // }; 10 | 11 | /* unique value can only be accessed with: */ 12 | // obj[unique]; // unique value 13 | -------------------------------------------------------------------------------- /Chapter 03/undefined.ts: -------------------------------------------------------------------------------- 1 | /* +--------------------Undefined--------------------+ */ 2 | 3 | // let notYet: undefined = undefined; 4 | -------------------------------------------------------------------------------- /Chapter 03/union.ts: -------------------------------------------------------------------------------- 1 | /* +--------------------Union Types--------------------+ */ 2 | 3 | //let either: string | number = 1; 4 | 5 | // either = true; 6 | 7 | // function getRefNumber(refNumber: string | number) { 8 | // if (typeof refNumber === 'string') { 9 | // refNumber = parseInt(refNumber, 10); 10 | // } 11 | 12 | // return refNumber; 13 | // } 14 | -------------------------------------------------------------------------------- /Chapter 03/unknown.ts: -------------------------------------------------------------------------------- 1 | /* +--------------------Unknown--------------------+ */ 2 | 3 | // let notKnown: unknown; 4 | 5 | // notKnown.test = 'fail'; 6 | // notKnown(); 7 | // notKnown = { test: 'test' }; 8 | // notKnown = () => 'test'; 9 | // notKnown = [1, 2, 3]; 10 | 11 | /* all produce errors */ 12 | // notKnown.test'; 13 | // notKnown(); 14 | // notKnown[0]; 15 | 16 | // notKnown = 'test'; 17 | // if (notKnown === 'test') { 18 | // console.log('we will see this log message'); 19 | // } 20 | -------------------------------------------------------------------------------- /Chapter 04/my-lib/my-lib.ts: -------------------------------------------------------------------------------- 1 | export const myLib = { 2 | _version: '0.0.1', 3 | version: () => myLib._version, 4 | reverseString: (str: string) => str.split('').reverse().join('') 5 | } 6 | -------------------------------------------------------------------------------- /Chapter 04/my-lib/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "../../tsconfig.json", 3 | "compilerOptions": { 4 | "declaration": true, 5 | "outDir": "./dist" 6 | } 7 | } 8 | -------------------------------------------------------------------------------- /Chapter 04/old-lib/src/old-lib.d.ts: -------------------------------------------------------------------------------- 1 | declare namespace oldLib { 2 | function reverse(str: string): string; 3 | } 4 | 5 | -------------------------------------------------------------------------------- /Chapter 04/old-lib/src/old-lib.js: -------------------------------------------------------------------------------- 1 | var oldLib = { 2 | /** 3 | * Reverse a string 4 | * @param {string} str The string to reverse 5 | * @returns {string} The reversed string 6 | */ 7 | reverse: function(str) { 8 | return str.split('').reverse().join(''); 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /Chapter 04/old-lib/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "allowJs": true, 4 | "checkJs": true, 5 | "declaration": true, 6 | "emitDeclarationOnly": true 7 | } 8 | } 9 | -------------------------------------------------------------------------------- /Chapter 04/projects/main-project/src/main-source-file-1.ts: -------------------------------------------------------------------------------- 1 | const mainExample: string = 'This is an example'; 2 | -------------------------------------------------------------------------------- /Chapter 04/projects/main-project/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "references": [ 3 | { "path": "../sub-project-1" }, 4 | { "path": "../sub-project-2" }, 5 | ], 6 | "compilerOptions": { 7 | /* Visit https://aka.ms/tsconfig to read more about this file */ 8 | 9 | /* Projects */ 10 | // "incremental": true, /* Save .tsbuildinfo files to allow for incremental compilation of projects. */ 11 | // "composite": true, /* Enable constraints that allow a TypeScript project to be used with project references. */ 12 | // "tsBuildInfoFile": "./.tsbuildinfo", /* Specify the path to .tsbuildinfo incremental compilation file. */ 13 | // "disableSourceOfProjectReferenceRedirect": true, /* Disable preferring source files instead of declaration files when referencing composite projects. */ 14 | // "disableSolutionSearching": true, /* Opt a project out of multi-project reference checking when editing. */ 15 | // "disableReferencedProjectLoad": true, /* Reduce the number of projects loaded automatically by TypeScript. */ 16 | 17 | /* Language and Environment */ 18 | "target": "es2022", /* Set the JavaScript language version for emitted JavaScript and include compatible library declarations. */ 19 | // "lib": [], /* Specify a set of bundled library declaration files that describe the target runtime environment. */ 20 | // "jsx": "preserve", /* Specify what JSX code is generated. */ 21 | // "experimentalDecorators": true, /* Enable experimental support for TC39 stage 2 draft decorators. */ 22 | // "emitDecoratorMetadata": true, /* Emit design-type metadata for decorated declarations in source files. */ 23 | // "jsxFactory": "", /* Specify the JSX factory function used when targeting React JSX emit, e.g. 'React.createElement' or 'h'. */ 24 | // "jsxFragmentFactory": "", /* Specify the JSX Fragment reference used for fragments when targeting React JSX emit e.g. 'React.Fragment' or 'Fragment'. */ 25 | // "jsxImportSource": "", /* Specify module specifier used to import the JSX factory functions when using 'jsx: react-jsx*'. */ 26 | // "reactNamespace": "", /* Specify the object invoked for 'createElement'. This only applies when targeting 'react' JSX emit. */ 27 | // "noLib": true, /* Disable including any library files, including the default lib.d.ts. */ 28 | // "useDefineForClassFields": true, /* Emit ECMAScript-standard-compliant class fields. */ 29 | // "moduleDetection": "auto", /* Control what method is used to detect module-format JS files. */ 30 | 31 | /* Modules */ 32 | "module": "ES2022", /* Specify what module code is generated. */ 33 | "rootDir": "../", /* Specify the root folder within your source files. */ 34 | // "moduleResolution": "node", /* Specify how TypeScript looks up a file from a given module specifier. */ 35 | // "baseUrl": "./", /* Specify the base directory to resolve non-relative module names. */ 36 | // "paths": {}, /* Specify a set of entries that re-map imports to additional lookup locations. */ 37 | // "rootDirs": [], /* Allow multiple folders to be treated as one when resolving modules. */ 38 | // "typeRoots": [], /* Specify multiple folders that act like './node_modules/@types'. */ 39 | // "types": [], /* Specify type package names to be included without being referenced in a source file. */ 40 | // "allowUmdGlobalAccess": true, /* Allow accessing UMD globals from modules. */ 41 | // "moduleSuffixes": [], /* List of file name suffixes to search when resolving a module. */ 42 | // "resolveJsonModule": true, /* Enable importing .json files. */ 43 | // "noResolve": true, /* Disallow 'import's, 'require's or ''s from expanding the number of files TypeScript should add to a project. */ 44 | 45 | /* JavaScript Support */ 46 | // "allowJs": true, /* Allow JavaScript files to be a part of your program. Use the 'checkJS' option to get errors from these files. */ 47 | // "checkJs": true, /* Enable error reporting in type-checked JavaScript files. */ 48 | // "maxNodeModuleJsDepth": 1, /* Specify the maximum folder depth used for checking JavaScript files from 'node_modules'. Only applicable with 'allowJs'. */ 49 | 50 | /* Emit */ 51 | // "declaration": true, /* Generate .d.ts files from TypeScript and JavaScript files in your project. */ 52 | // "declarationMap": true, /* Create sourcemaps for d.ts files. */ 53 | // "emitDeclarationOnly": true, /* Only output d.ts files and not JavaScript files. */ 54 | // "sourceMap": true, /* Create source map files for emitted JavaScript files. */ 55 | // "outFile": "./", /* Specify a file that bundles all outputs into one JavaScript file. If 'declaration' is true, also designates a file that bundles all .d.ts output. */ 56 | "outDir": "./dist", /* Specify an output folder for all emitted files. */ 57 | // "removeComments": true, /* Disable emitting comments. */ 58 | // "noEmit": true, /* Disable emitting files from a compilation. */ 59 | // "importHelpers": true, /* Allow importing helper functions from tslib once per project, instead of including them per-file. */ 60 | // "importsNotUsedAsValues": "remove", /* Specify emit/checking behavior for imports that are only used for types. */ 61 | // "downlevelIteration": true, /* Emit more compliant, but verbose and less performant JavaScript for iteration. */ 62 | // "sourceRoot": "", /* Specify the root path for debuggers to find the reference source code. */ 63 | // "mapRoot": "", /* Specify the location where debugger should locate map files instead of generated locations. */ 64 | // "inlineSourceMap": true, /* Include sourcemap files inside the emitted JavaScript. */ 65 | // "inlineSources": true, /* Include source code in the sourcemaps inside the emitted JavaScript. */ 66 | // "emitBOM": true, /* Emit a UTF-8 Byte Order Mark (BOM) in the beginning of output files. */ 67 | // "newLine": "crlf", /* Set the newline character for emitting files. */ 68 | // "stripInternal": true, /* Disable emitting declarations that have '@internal' in their JSDoc comments. */ 69 | // "noEmitHelpers": true, /* Disable generating custom helper functions like '__extends' in compiled output. */ 70 | "noEmitOnError": true, /* Disable emitting files if any type checking errors are reported. */ 71 | // "preserveConstEnums": true, /* Disable erasing 'const enum' declarations in generated code. */ 72 | // "declarationDir": "./", /* Specify the output directory for generated declaration files. */ 73 | // "preserveValueImports": true, /* Preserve unused imported values in the JavaScript output that would otherwise be removed. */ 74 | 75 | /* Interop Constraints */ 76 | // "isolatedModules": true, /* Ensure that each file can be safely transpiled without relying on other imports. */ 77 | // "allowSyntheticDefaultImports": true, /* Allow 'import x from y' when a module doesn't have a default export. */ 78 | "esModuleInterop": true, /* Emit additional JavaScript to ease support for importing CommonJS modules. This enables 'allowSyntheticDefaultImports' for type compatibility. */ 79 | // "preserveSymlinks": true, /* Disable resolving symlinks to their realpath. This correlates to the same flag in node. */ 80 | "forceConsistentCasingInFileNames": true, /* Ensure that casing is correct in imports. */ 81 | 82 | /* Type Checking */ 83 | "strict": true, /* Enable all strict type-checking options. */ 84 | // "noImplicitAny": true, /* Enable error reporting for expressions and declarations with an implied 'any' type. */ 85 | // "strictNullChecks": true, /* When type checking, take into account 'null' and 'undefined'. */ 86 | // "strictFunctionTypes": true, /* When assigning functions, check to ensure parameters and the return values are subtype-compatible. */ 87 | // "strictBindCallApply": true, /* Check that the arguments for 'bind', 'call', and 'apply' methods match the original function. */ 88 | // "strictPropertyInitialization": true, /* Check for class properties that are declared but not set in the constructor. */ 89 | // "noImplicitThis": true, /* Enable error reporting when 'this' is given the type 'any'. */ 90 | // "useUnknownInCatchVariables": true, /* Default catch clause variables as 'unknown' instead of 'any'. */ 91 | // "alwaysStrict": true, /* Ensure 'use strict' is always emitted. */ 92 | // "noUnusedLocals": true, /* Enable error reporting when local variables aren't read. */ 93 | // "noUnusedParameters": true, /* Raise an error when a function parameter isn't read. */ 94 | // "exactOptionalPropertyTypes": true, /* Interpret optional property types as written, rather than adding 'undefined'. */ 95 | // "noImplicitReturns": true, /* Enable error reporting for codepaths that do not explicitly return in a function. */ 96 | // "noFallthroughCasesInSwitch": true, /* Enable error reporting for fallthrough cases in switch statements. */ 97 | // "noUncheckedIndexedAccess": true, /* Add 'undefined' to a type when accessed using an index. */ 98 | // "noImplicitOverride": true, /* Ensure overriding members in derived classes are marked with an override modifier. */ 99 | // "noPropertyAccessFromIndexSignature": true, /* Enforces using indexed accessors for keys declared using an indexed type. */ 100 | // "allowUnusedLabels": true, /* Disable error reporting for unused labels. */ 101 | // "allowUnreachableCode": true, /* Disable error reporting for unreachable code. */ 102 | 103 | /* Completeness */ 104 | // "skipDefaultLibCheck": true, /* Skip type checking .d.ts files that are included with TypeScript. */ 105 | "skipLibCheck": true, /* Skip type checking all .d.ts files. */ 106 | } 107 | } 108 | -------------------------------------------------------------------------------- /Chapter 04/projects/sub-project-1/src/sub-source-file-1.ts: -------------------------------------------------------------------------------- 1 | const subExample1: string = 'This is an example!'; 2 | -------------------------------------------------------------------------------- /Chapter 04/projects/sub-project-1/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "../main-project/tsconfig.json", 3 | "include": ["**/*.ts"], 4 | "compilerOptions": { 5 | "composite": true, 6 | } 7 | } 8 | -------------------------------------------------------------------------------- /Chapter 04/projects/sub-project-2/src/sub-source-file-2.ts: -------------------------------------------------------------------------------- 1 | const subExample2: string = 'This is an example!'; 2 | -------------------------------------------------------------------------------- /Chapter 04/projects/sub-project-2/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "../main-project/tsconfig.json", 3 | "include": ["**/*.ts"], 4 | "compilerOptions": { 5 | "composite": true 6 | } 7 | } 8 | -------------------------------------------------------------------------------- /Chapter 04/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "../tsconfig.json", 3 | "include": ["../**/*.ts"], 4 | "exclude": ["./webpack-integration", "my-lib"] 5 | } 6 | -------------------------------------------------------------------------------- /Chapter 04/webpack-integration/.babelrc: -------------------------------------------------------------------------------- 1 | { 2 | "presets": [ 3 | [ 4 | "@babel/preset-env", 5 | { 6 | "useBuiltIns": "usage", 7 | "corejs": "3.0.0" 8 | } 9 | ] 10 | ], 11 | "plugins": [ 12 | "@babel/plugin-syntax-dynamic-import", 13 | "@babel/plugin-proposal-class-properties" 14 | ] 15 | } 16 | -------------------------------------------------------------------------------- /Chapter 04/webpack-integration/.browserslistrc: -------------------------------------------------------------------------------- 1 | [production staging] 2 | >5% 3 | last 2 versions 4 | Firefox ESR 5 | not ie < 11 6 | 7 | [development] 8 | last 1 chrome version 9 | last 1 firefox version 10 | last 1 edge version 11 | -------------------------------------------------------------------------------- /Chapter 04/webpack-integration/.eslintrc: -------------------------------------------------------------------------------- 1 | { 2 | "parser": "@babel/eslint-parser", 3 | "extends": [ 4 | "eslint:recommended" 5 | ], 6 | "env": { 7 | "browser": true, 8 | "node": true 9 | }, 10 | "parserOptions": { 11 | "ecmaVersion": 8, 12 | "sourceType": "module", 13 | "requireConfigFile": false 14 | }, 15 | "rules": { 16 | "semi": 2 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /Chapter 04/webpack-integration/.github/FUNDING.yml: -------------------------------------------------------------------------------- 1 | # These are supported funding model platforms 2 | 3 | github: [wbkd] 4 | -------------------------------------------------------------------------------- /Chapter 04/webpack-integration/.github/dependabot.yml: -------------------------------------------------------------------------------- 1 | version: 2 2 | updates: 3 | - package-ecosystem: npm 4 | directory: "/" 5 | schedule: 6 | interval: weekly 7 | time: "04:00" 8 | open-pull-requests-limit: 10 9 | ignore: 10 | - dependency-name: eslint 11 | versions: 12 | - 7.18.0 13 | - 7.19.0 14 | - 7.20.0 15 | - 7.21.0 16 | - 7.22.0 17 | - 7.23.0 18 | - 7.24.0 19 | - dependency-name: mini-css-extract-plugin 20 | versions: 21 | - 1.3.4 22 | - 1.3.5 23 | - 1.3.6 24 | - 1.3.8 25 | - 1.3.9 26 | - 1.4.0 27 | - 1.4.1 28 | - dependency-name: "@babel/core" 29 | versions: 30 | - 7.12.10 31 | - 7.12.13 32 | - 7.12.16 33 | - 7.12.17 34 | - 7.13.10 35 | - 7.13.13 36 | - 7.13.14 37 | - 7.13.15 38 | - 7.13.8 39 | - dependency-name: copy-webpack-plugin 40 | versions: 41 | - 7.0.0 42 | - 8.0.0 43 | - 8.1.0 44 | - dependency-name: "@babel/preset-env" 45 | versions: 46 | - 7.12.11 47 | - 7.12.13 48 | - 7.12.16 49 | - 7.12.17 50 | - 7.13.10 51 | - 7.13.12 52 | - 7.13.8 53 | - 7.13.9 54 | - dependency-name: webpack-cli 55 | versions: 56 | - 4.4.0 57 | - 4.5.0 58 | - dependency-name: webpack 59 | versions: 60 | - 5.17.0 61 | - 5.19.0 62 | -------------------------------------------------------------------------------- /Chapter 04/webpack-integration/.gitignore: -------------------------------------------------------------------------------- 1 | # dependencies 2 | node_modules 3 | 4 | # production 5 | build 6 | 7 | # misc 8 | .DS_Store 9 | 10 | npm-debug.log 11 | yarn-error.log 12 | yarn.lock 13 | .yarnclean 14 | .vscode 15 | .idea 16 | -------------------------------------------------------------------------------- /Chapter 04/webpack-integration/.stylelintrc.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "stylelint-config-standard" 3 | } 4 | -------------------------------------------------------------------------------- /Chapter 04/webpack-integration/.travis.yml: -------------------------------------------------------------------------------- 1 | language: node_js 2 | node_js: 3 | - "lts/*" 4 | - "node" 5 | script: npm run build 6 | -------------------------------------------------------------------------------- /Chapter 04/webpack-integration/LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License 2 | 3 | Copyright (c) 2020 webkid GmbH 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 13 | all 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 21 | THE SOFTWARE. 22 | -------------------------------------------------------------------------------- /Chapter 04/webpack-integration/README.md: -------------------------------------------------------------------------------- 1 | # Webpack Frontend Starterkit 2 | 3 | A lightweight foundation for your next webpack based frontend project. 4 | 5 | ### Installation 6 | 7 | ```sh 8 | npm install 9 | ``` 10 | 11 | ### Start Dev Server 12 | 13 | ```sh 14 | npm start 15 | ``` 16 | 17 | ### Build Prod Version 18 | 19 | ```sh 20 | npm run build 21 | ``` 22 | 23 | ### Features: 24 | 25 | - ES6 Support via [babel](https://babeljs.io/) (v7) 26 | - JavaScript Linting via [eslint](https://eslint.org/) 27 | - SASS Support via [sass-loader](https://github.com/jtangelder/sass-loader) 28 | - Autoprefixing of browserspecific CSS rules via [postcss](https://postcss.org/) and [postcss-preset-env](https://github.com/csstools/postcss-preset-env) 29 | - Style Linting via [stylelint](https://stylelint.io/) 30 | 31 | When you run `npm run build` we use the [mini-css-extract-plugin](https://github.com/webpack-contrib/mini-css-extract-plugin) to move the css to a separate file. The css file gets included in the head of the `index.html`. 32 | -------------------------------------------------------------------------------- /Chapter 04/webpack-integration/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "webpack-starter", 3 | "version": "1.0.0", 4 | "description": "A light foundation for your next frontend project based on webpack.", 5 | "scripts": { 6 | "lint": "npm run lint:styles; npm run lint:scripts", 7 | "lint:styles": "stylelint src", 8 | "lint:scripts": "eslint src", 9 | "build": "cross-env NODE_ENV=production webpack --config webpack/webpack.config.prod.js", 10 | "start": "webpack serve --config webpack/webpack.config.dev.js" 11 | }, 12 | "repository": { 13 | "type": "git", 14 | "url": "git+https://github.com/wbkd/webpack-starter.git" 15 | }, 16 | "keywords": [ 17 | "webpack", 18 | "startkit", 19 | "frontend", 20 | "es6", 21 | "javascript", 22 | "webdev" 23 | ], 24 | "author": "webkid.io", 25 | "license": "MIT", 26 | "bugs": { 27 | "url": "https://github.com/wbkd/webpack-starter/issues" 28 | }, 29 | "devDependencies": { 30 | "@babel/core": "^7.16.7", 31 | "@babel/eslint-parser": "^7.16.5", 32 | "@babel/plugin-proposal-class-properties": "^7.16.7", 33 | "@babel/plugin-syntax-dynamic-import": "^7.8.3", 34 | "@babel/preset-env": "^7.16.8", 35 | "@types/jquery": "^3.5.14", 36 | "babel-loader": "^8.2.3", 37 | "clean-webpack-plugin": "^4.0.0", 38 | "copy-webpack-plugin": "^10.2.0", 39 | "cross-env": "^7.0.3", 40 | "css-loader": "^6.5.1", 41 | "eslint": "^8.7.0", 42 | "eslint-webpack-plugin": "^3.1.1", 43 | "file-loader": "^6.2.0", 44 | "html-loader": "^3.1.0", 45 | "html-webpack-plugin": "^5.5.0", 46 | "mini-css-extract-plugin": "^2.5.1", 47 | "node-sass": "^7.0.1", 48 | "postcss-loader": "^6.2.1", 49 | "postcss-preset-env": "^7.2.3", 50 | "sass-loader": "^12.4.0", 51 | "style-loader": "^3.3.1", 52 | "stylelint": "^14.2.0", 53 | "stylelint-config-standard": "^24.0.0", 54 | "stylelint-webpack-plugin": "^3.1.1", 55 | "ts-loader": "^9.3.1", 56 | "webpack": "^5.66.0", 57 | "webpack-cli": "^4.9.1", 58 | "webpack-dev-server": "^4.7.3", 59 | "webpack-merge": "^5.8.0" 60 | }, 61 | "dependencies": { 62 | "@babel/polyfill": "^7.12.1", 63 | "core-js": "^3.20.3", 64 | "jquery": "^3.6.1" 65 | } 66 | } 67 | -------------------------------------------------------------------------------- /Chapter 04/webpack-integration/postcss.config.js: -------------------------------------------------------------------------------- 1 | const postcssPresetEnv = require('postcss-preset-env'); 2 | 3 | module.exports = { 4 | plugins: [postcssPresetEnv()], 5 | }; 6 | -------------------------------------------------------------------------------- /Chapter 04/webpack-integration/public/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/OrangeAVA/Ultimate-Typescript-Handbook/3345748735cfa2866a88248587fc3ecd689dbf04/Chapter 04/webpack-integration/public/.gitkeep -------------------------------------------------------------------------------- /Chapter 04/webpack-integration/src/images/webpack.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/OrangeAVA/Ultimate-Typescript-Handbook/3345748735cfa2866a88248587fc3ecd689dbf04/Chapter 04/webpack-integration/src/images/webpack.png -------------------------------------------------------------------------------- /Chapter 04/webpack-integration/src/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | webpack starterkit 7 | 8 | 9 | 10 | Webpack logo 11 |

webpack starter

12 |

✨ A lightweight foundation for your next webpack based frontend project.

13 | 14 | 15 | -------------------------------------------------------------------------------- /Chapter 04/webpack-integration/src/scripts/app.ts: -------------------------------------------------------------------------------- 1 | import jquery from 'jquery'; 2 | export const app = { name: 'my-app', jqueryVersion: jquery.fn.jquery }; 3 | -------------------------------------------------------------------------------- /Chapter 04/webpack-integration/src/scripts/index.ts: -------------------------------------------------------------------------------- 1 | import '../styles/index.scss'; 2 | import { app } from './app'; 3 | 4 | console.log('App running: ', app.name); 5 | -------------------------------------------------------------------------------- /Chapter 04/webpack-integration/src/styles/index.scss: -------------------------------------------------------------------------------- 1 | $body-color: slateblue; 2 | 3 | body { 4 | color: $body-color; 5 | font-family: sans-serif; 6 | 7 | img { 8 | margin-top: -3rem; 9 | margin-bottom: -4rem; 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /Chapter 04/webpack-integration/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "../../tsconfig.json", 3 | "include": ["./src/**/*.ts"], 4 | } 5 | -------------------------------------------------------------------------------- /Chapter 04/webpack-integration/webpack/webpack.common.js: -------------------------------------------------------------------------------- 1 | const Path = require('path'); 2 | const { CleanWebpackPlugin } = require('clean-webpack-plugin'); 3 | const CopyWebpackPlugin = require('copy-webpack-plugin'); 4 | const HtmlWebpackPlugin = require('html-webpack-plugin'); 5 | 6 | module.exports = { 7 | entry: { 8 | app: Path.resolve(__dirname, '../src/scripts/index.ts'), 9 | }, 10 | output: { 11 | path: Path.join(__dirname, '../build'), 12 | filename: 'js/[name].js', 13 | }, 14 | optimization: { 15 | splitChunks: { 16 | chunks: 'all', 17 | name: false, 18 | }, 19 | }, 20 | plugins: [ 21 | new CleanWebpackPlugin(), 22 | new CopyWebpackPlugin({ 23 | patterns: [{ from: Path.resolve(__dirname, '../public'), to: 'public' }], 24 | }), 25 | new HtmlWebpackPlugin({ 26 | template: Path.resolve(__dirname, '../src/index.html'), 27 | }), 28 | ], 29 | resolve: { 30 | alias: { 31 | '~': Path.resolve(__dirname, '../src'), 32 | }, 33 | extensions: ['.ts', '.js'], 34 | }, 35 | module: { 36 | rules: [ 37 | { 38 | test: /\.ts$/, 39 | use: 'ts-loader', 40 | exclude: /node_modules/, 41 | }, 42 | { 43 | test: /\.html$/i, 44 | loader: 'html-loader', 45 | }, 46 | { 47 | test: /\.(ico|jpg|jpeg|png|gif|eot|otf|webp|svg|ttf|woff|woff2)(\?.*)?$/, 48 | type: 'asset' 49 | }, 50 | ], 51 | }, 52 | }; 53 | -------------------------------------------------------------------------------- /Chapter 04/webpack-integration/webpack/webpack.config.dev.js: -------------------------------------------------------------------------------- 1 | const Path = require('path'); 2 | const Webpack = require('webpack'); 3 | const { merge } = require('webpack-merge'); 4 | const ESLintPlugin = require('eslint-webpack-plugin'); 5 | const StylelintPlugin = require('stylelint-webpack-plugin'); 6 | 7 | const common = require('./webpack.common.js'); 8 | 9 | module.exports = merge(common, { 10 | target: 'web', 11 | mode: 'development', 12 | devtool: 'eval-cheap-source-map', 13 | output: { 14 | chunkFilename: 'js/[name].chunk.js', 15 | }, 16 | devServer: { 17 | client: { 18 | logging: 'error', 19 | }, 20 | hot: true, 21 | }, 22 | plugins: [ 23 | new Webpack.DefinePlugin({ 24 | 'process.env.NODE_ENV': JSON.stringify('development'), 25 | }), 26 | new ESLintPlugin({ 27 | extensions: 'js', 28 | emitWarning: true, 29 | files: Path.resolve(__dirname, '../src'), 30 | }), 31 | new StylelintPlugin({ 32 | files: Path.join('src', '**/*.s?(a|c)ss'), 33 | }), 34 | ], 35 | module: { 36 | rules: [ 37 | { 38 | test: /\.js$/, 39 | include: Path.resolve(__dirname, '../src'), 40 | loader: 'babel-loader', 41 | }, 42 | { 43 | test: /\.s?css$/i, 44 | use: [ 45 | 'style-loader', 46 | { 47 | loader: 'css-loader', 48 | options: { 49 | sourceMap: true, 50 | }, 51 | }, 52 | 'postcss-loader', 53 | 'sass-loader', 54 | ], 55 | }, 56 | ], 57 | }, 58 | }); 59 | -------------------------------------------------------------------------------- /Chapter 04/webpack-integration/webpack/webpack.config.prod.js: -------------------------------------------------------------------------------- 1 | const Webpack = require('webpack'); 2 | const { merge } = require('webpack-merge'); 3 | const MiniCssExtractPlugin = require('mini-css-extract-plugin'); 4 | const common = require('./webpack.common.js'); 5 | 6 | module.exports = merge(common, { 7 | mode: 'production', 8 | devtool: 'source-map', 9 | stats: 'errors-only', 10 | bail: true, 11 | output: { 12 | filename: 'js/[name].[chunkhash:8].js', 13 | chunkFilename: 'js/[name].[chunkhash:8].chunk.js', 14 | }, 15 | plugins: [ 16 | new Webpack.DefinePlugin({ 17 | 'process.env.NODE_ENV': JSON.stringify('production'), 18 | }), 19 | new MiniCssExtractPlugin({ 20 | filename: 'css/[name].[chunkhash:8].css', 21 | chunkFilename: 'css/[name].[chunkhash:8].chunk.js', 22 | }), 23 | ], 24 | module: { 25 | rules: [ 26 | { 27 | test: /\.js$/, 28 | exclude: /node_modules/, 29 | use: 'babel-loader', 30 | }, 31 | { 32 | test: /\.s?css/i, 33 | use: [MiniCssExtractPlugin.loader, 'css-loader', 'postcss-loader', 'sass-loader'], 34 | }, 35 | ], 36 | }, 37 | }); 38 | -------------------------------------------------------------------------------- /Chapter 05/enums.ts: -------------------------------------------------------------------------------- 1 | /* +--------------------Numeric Enums--------------------+ */ 2 | 3 | // enum Fruit { 4 | // Apple, 5 | // Blackberry, 6 | // Melon, 7 | // }; 8 | 9 | //console.log(Fruit.Blackberry); // 1 10 | 11 | // enum Fruit { 12 | // Apple = 5, 13 | // Blackberry = 6, 14 | // Melon = 7, 15 | // }; 16 | 17 | // function determinePortionSize(fruit: Fruit) { 18 | // switch (fruit) { 19 | // case Fruit.Apple: 20 | // return 1; 21 | // case Fruit.Blackberry: 22 | // return 10; 23 | // case Fruit.Melon: 24 | // return 0.5; 25 | // } 26 | // }; 27 | // determinePortionSize(Fruit.Blackberry); // 10 28 | // determinePortionSize(6); // 10 29 | // determinePortionSize(60); // undefined 30 | 31 | // enum Fruit { 32 | // Apple = 5, 33 | // Blackberry, 34 | // Melon, 35 | // }; 36 | 37 | // enum Fruit { 38 | // Apple = -5, 39 | // Blackberry = 6 * 2, 40 | // Melon = 7 ^ 2, 41 | // }; 42 | 43 | // const enum TropicalFruit { 44 | // Mango, 45 | // Papaya, 46 | // Melon = Fruit.Melon, 47 | // }; 48 | 49 | // function returnTen() { return 10 } 50 | 51 | // enum Drupes { 52 | // Plum = 5, 53 | // Peach = 5, 54 | // }; 55 | 56 | /* +--------------------Reverse Mapping--------------------+ */ 57 | 58 | // console.log(Fruit[5]); // Apple 59 | 60 | /* +--------------------Exhaustiveness and the never Type--------------------+ */ 61 | 62 | // function handleUnknownMember(member: never): never { 63 | // throw new Error('Unhandled enum member: ' + Fruit[member]); 64 | // } 65 | 66 | // function determinePortionSize(fruit: Fruit) { 67 | // switch (fruit) { 68 | // case Fruit.Apple: 69 | // return 1; 70 | // case Fruit.Blackberry: 71 | // return 10; 72 | // case Fruit.Melon: 73 | // return 0.5; 74 | // case Fruit.Banana: 75 | // return 1; 76 | // default: 77 | // handleUnknownMember(fruit as never); 78 | // } 79 | // } 80 | // determinePortionSize(Fruit.Banana); 81 | // console.log(determinePortionSize(Fruit.Blackberry)); // 10 82 | 83 | // enum Fruit { 84 | // Apple = 5, 85 | // Blackberry = 6, 86 | // Melon = 7, 87 | // Banana = 8, 88 | // }; 89 | 90 | /* +--------------------String Enums--------------------+ */ 91 | 92 | // enum Vegetables { 93 | // Pea = 'PEA', 94 | // Potato = 'POTATO', 95 | // Cabbage = 'CABBAGE', 96 | // }; 97 | 98 | // function peelVegetable(vegetable: Vegetables) { 99 | // // do something 100 | // } 101 | 102 | // peelVegetable('Potato'); // Not fine 103 | // peelVegetable(Vegetables.Potato); // Fine 104 | 105 | /* +--------------------Heterogeneous Enums--------------------+ */ 106 | 107 | // enum Vegetables { 108 | // Pea = 'PEA', 109 | // Potato = 0, 110 | // Cabbage, 111 | // Corn = 'CORN', 112 | // Parsnip = 5, 113 | // Spinach, 114 | // }; 115 | // console.log(Vegetables.Spinach); // 6 116 | 117 | // enum Vegetables2 { 118 | // Onion = 'Onion', 119 | // Kale = Vegetables.Cabbage, 120 | // }; 121 | 122 | /* +--------------------Computed Enums--------------------+ */ 123 | 124 | // enum TropicalFruit { 125 | // Mango, 126 | // Papaya, 127 | // Melon = Fruit.Melon, 128 | // PassionFruit = returnTen() 129 | // }; 130 | // function returnTen() { return 10 } 131 | 132 | // enum Vegetables2 { 133 | // Onion = 'Onion', 134 | // Kale = Vegetables.Cabbage, // error 135 | // }; 136 | 137 | /* +--------------------Literal Enums--------------------+ */ 138 | 139 | // interface Legume { 140 | // vegetable: Vegetables.Pea; 141 | // }; 142 | 143 | // interface Tuber { 144 | // vegetable: Vegetables.Potato; 145 | // }; 146 | 147 | // const myTuber: Tuber = { 148 | // vegetable: Vegetables.Pea, 149 | // }; 150 | 151 | // function grow(seed: Vegetables) { 152 | // if (seed !== Vegetables.Pea || seed !== Vegetables.Cabbage) { 153 | // // plant Potato 154 | // } 155 | // } 156 | 157 | /* +--------------------Inlining Enums--------------------+ */ 158 | 159 | // const enum Tools { 160 | // Hammer, 161 | // Drill, 162 | // }; 163 | 164 | // function doItYourself(tool: Tools) { 165 | // if (tool === Tools.Hammer) { 166 | // console.log('Hammer time'); 167 | // } 168 | // } 169 | 170 | // doItYourself(Tools.Hammer); // Hammer time 171 | 172 | /* +--------------------Using the keyof Operator--------------------+ */ 173 | 174 | // type FruitKeys = keyof typeof Fruit; 175 | // const fruits: FruitKeys = 'Apple'; 176 | -------------------------------------------------------------------------------- /Chapter 05/interfaces.ts: -------------------------------------------------------------------------------- 1 | /* +--------------------Interfaces--------------------+ */ 2 | 3 | // interface Person { 4 | // name: string; 5 | // age: number; 6 | // }; 7 | 8 | // const me: Person = { 9 | // name: 'Dan', 10 | // age: 44, 11 | // interests: ['TypeScript'], 12 | // }; 13 | 14 | // interface Person { 15 | // readonly name: string; 16 | // age: number; 17 | // interests?: string[]; 18 | // }; 19 | 20 | // const me: Person = { 21 | // name: 'Dan', 22 | // age: 44, 23 | // }; 24 | 25 | // me.name = 'Bill'; // error 26 | 27 | // interface AnyProps { 28 | // [key: string]: any; 29 | // }; 30 | 31 | // const random: AnyProps = { 32 | // literallyAnything: 'Just has to be a string', 33 | // }; 34 | 35 | // interface PersonName { 36 | // firstName: string; 37 | // familyName: string; 38 | // otherNames?: string[]; 39 | // }; 40 | 41 | /* +--------------------Interface Merging--------------------+ */ 42 | 43 | // interface Person { 44 | // name: PersonName; 45 | // age: number; 46 | // interests?: string[]; 47 | // }; 48 | 49 | // interface Person { 50 | // height?: number; 51 | // }; 52 | 53 | // const me: Person = { 54 | // name: { 55 | // firstName: 'Dan', 56 | // familyName: 'Wellman', 57 | // }, 58 | // age: 44, 59 | // interests: ['Swimming'], 60 | // }; 61 | 62 | /* +--------------------Extending Interfaces--------------------+ */ 63 | 64 | // interface Developer extends Person { 65 | // languages: string[]; 66 | // }; 67 | 68 | // const dev: Developer = { 69 | // name: { 70 | // firstName: 'Dan', 71 | // familyName: 'Wellman', 72 | // }, 73 | // age: 44, 74 | // languages: ['TypeScript', 'JavaScript'], 75 | // }; 76 | 77 | // interface FirstAider { 78 | // cprTrained: boolean; 79 | // }; 80 | 81 | // interface DevManager extends Developer, FirstAider { 82 | // // members from all Person, Developer, and FirstAider interfaces 83 | // }; 84 | 85 | // interface MethodTest { 86 | // method(arg0: string): string; 87 | // }; 88 | 89 | // interface MethodTest { 90 | // method(arg0: number): number; 91 | // }; 92 | -------------------------------------------------------------------------------- /Chapter 05/namespaces.ts: -------------------------------------------------------------------------------- 1 | /* +--------------------Namespaces--------------------+ */ 2 | 3 | // namespace StringUtils { 4 | // export const reverse = (str: string) => str.split('').reverse().join(''); 5 | // }; 6 | // namespace NumberUtils { 7 | // export const reverse = (num: number) => parseInt(num.toString().split('').reverse().join(''), 10); 8 | // }; 9 | 10 | // StringUtils.reverse('abc'); // cba 11 | // NumberUtils.reverse(123); // 321 12 | 13 | /* +--------------------Namespace Merging--------------------+ */ 14 | 15 | // namespace NsTest { 16 | // export const str = 'abc' 17 | // }; 18 | // namespace NsTest { 19 | // export const str = 123; 20 | // }; 21 | 22 | // NsTest.str; // abc 23 | // NsTest.num; // 123 24 | -------------------------------------------------------------------------------- /Chapter 06/arrays.ts: -------------------------------------------------------------------------------- 1 | /* +--------------------Arrays--------------------+ */ 2 | 3 | // const strings: string[] = []; 4 | // const strings: Array = ['test']; 5 | 6 | // enum Status { 7 | // Waiting, 8 | // Complete, 9 | // } 10 | 11 | // const statuses: Status[] = [Status.Waiting]; 12 | 13 | // interface Process { 14 | // name: string, 15 | // status: Status, 16 | // } 17 | 18 | // const processes: Process[] = [{ name: 'Process 1', status: Status.Waiting }]; 19 | // const strings: Array = []; 20 | 21 | // const codes: (string|number)[] = [1, '2']; 22 | // const codes: Array = [1, '2']; 23 | // const codes: Array = [1, '2']; 24 | 25 | /* +--------------------Array Type Inference--------------------+ */ 26 | 27 | // const ids = ['1A', '1B']; // string[] 28 | // const altIds = [1, '1a']; // (string|number)[] 29 | // const processes = [{ name: 'Process 1', status: Status.Waiting }]; // Process[] 30 | 31 | /* +--------------------Read-only Arrays--------------------+ */ 32 | 33 | // const unmodifiable: readonly string[] = ['cannot be changed']; 34 | // const unmodifiable: readonly Array = ['cannot be changed']; // error 35 | // unmodifiable.push('oops'); // error 36 | // unmodifiable[0] = 'cannot do it'; 37 | 38 | // const unmodifiable2: ReadonlyArray = ['immutable']; 39 | -------------------------------------------------------------------------------- /Chapter 06/objects.ts: -------------------------------------------------------------------------------- 1 | /* +--------------------Object Types--------------------+ */ 2 | 3 | // let car: { doors: number, make: string }; 4 | // car = { doors: 5 }; // error 5 | // car = { doors: 5, make: 'Tesla', oops: 'whoops' }; // error 6 | // car = { doors: 5, make: 2 }; // error 7 | // car = { doors: 5, make: 'Tesla' }; // perfect 8 | 9 | // let car: { drive: (arg0: string) => void }; 10 | // car = { 11 | // drive: (direction: string) => { 12 | // // drive in the direction 13 | // }, 14 | // }; 15 | 16 | // const result = car.drive('forward'); 17 | // if (result) { 18 | // error 19 | // }; 20 | 21 | /* +--------------------Property Modifiers--------------------+ */ 22 | 23 | // interface Car { 24 | // doors?: number; 25 | // readonly make: string; 26 | // }; 27 | // const car2: Car = { make: 'Porsche' }; 28 | 29 | /* +--------------------Index Signatures--------------------+ */ 30 | 31 | // interface Car { 32 | // [prop: string]: string | boolean | number; 33 | // readonly [id: number]: number; 34 | // }; 35 | // const car3: Car = { make: 'Lotus', spoiler: true }; 36 | // const car4: Car = { make: 'Lotus', 0: 168 }; 37 | // const topSpeedMph = car4[0]; // 168 38 | // car4[0] = 200; // error 39 | 40 | /* +--------------------Intersections--------------------+ */ 41 | 42 | // interface Vinyl { 43 | // rpm?: number; 44 | // title: string; 45 | // }; 46 | // interface Cd { 47 | // trackNumber?: number; 48 | // title: string; 49 | // }; 50 | 51 | // interface CircularMedia extends Vinyl, Cd {} 52 | 53 | // const album: CircularMedia = { 54 | // rpm: 45, 55 | // title: 'Dark Side of the Moon', 56 | // } 57 | //interface CircularMedia extends Vinyl, Cd {} 58 | // const album: CircularMedia = { 59 | // rpm: 45, 60 | // title: 'Dark Side of the Moon', 61 | // } 62 | 63 | // const album: Vinyl & Cd = { 64 | // rpm: 45, 65 | // title: 'Dark Side of the Moon' 66 | // }; 67 | // type CircularMedia = Vinyl & Cd; 68 | 69 | // type mp3 = { bitrate?: number }; 70 | // type mp4 = { encoding?: string }; 71 | // type digitalMedia = mp3 & mp4; 72 | 73 | /* +--------------------Generic Object Types--------------------+ */ 74 | 75 | // interface Warning { 76 | // level: string; 77 | // text: string; 78 | // } 79 | // interface Info { 80 | // level: string; 81 | // text: string; 82 | // } 83 | // interface Modal { 84 | // message: Type; 85 | // } 86 | 87 | // const dataLoss: Warning = { 88 | // level: 'error', 89 | // text: 'Data may be lost, continue?', 90 | // }; 91 | // const completed: Info = { 92 | // level: 'info', 93 | // text: 'Process complete', 94 | // }; 95 | // const dataLossModal: Modal = { message: dataLoss }; 96 | // const saveFinished: Modal = { message: completed }; 97 | 98 | // type Modal = { 99 | // message: Type; 100 | // }; 101 | // type Warning = { 102 | // level: string; 103 | // text: string; 104 | // }; 105 | // type Info = { 106 | // level: string; 107 | // text: string; 108 | // }; 109 | 110 | // const dataLoss: Warning = { 111 | // level: 'error', 112 | // text: 'Data may be lost, continue?', 113 | // }; 114 | // const completed: Info = { 115 | // level: 'info', 116 | // text: 'Process complete', 117 | // }; 118 | // const dataLossModal: Modal = { message: dataLoss }; 119 | // const completedModal: Modal = { message: completed }; 120 | 121 | /* +--------------------Readonly Utility Type--------------------+ */ 122 | 123 | // interface ReadWrite { prop: string }; 124 | // let writable: ReadWrite = { prop: 'init'}; 125 | // writable.prop = 'can overwrite'; 126 | 127 | // let notWritable: Readonly; 128 | // notWritable = { prop: 'init' }; 129 | // notWritable.prop = 'nope'; // error 130 | -------------------------------------------------------------------------------- /Chapter 06/tuples.ts: -------------------------------------------------------------------------------- 1 | /* +--------------------Tuples--------------------+ */ 2 | 3 | // const category: [number, string, boolean] = [1, '1A', true]; 4 | 5 | // category[0] = '1'; // error 6 | // category[3] = false; // error 7 | // category.length = 4; // error 8 | // category.push(1); // fine 9 | 10 | // const category2: [id: number, subcategory: string, archived: boolean] = [1, '1A', true]; 11 | // const id = category2[0]; 12 | 13 | /* +--------------------Optional Elements--------------------+ */ 14 | 15 | // const category3: [number, string, boolean?] = [1, '1A', true]; 16 | 17 | /* +--------------------Rest Elements--------------------+ */ 18 | 19 | // let category4: [number, ...string[], boolean]; 20 | // category4 = [1, '1A', '1B', '1C', true]; 21 | // category4 = [1, true]; 22 | 23 | /* +--------------------Read-only Tuples--------------------+ */ 24 | 25 | // let category5: readonly [number, string, boolean] = [1, '1A', true]; 26 | // category5[0] = 5; // error 27 | -------------------------------------------------------------------------------- /Chapter 07/functions.ts: -------------------------------------------------------------------------------- 1 | /* +--------------------Functions--------------------+ */ 2 | // function sayHello(name: string): string { 3 | // return `Hello ${name}`; 4 | // } 5 | 6 | // sayHello(123); // error 7 | // sayHello(); // error 8 | // const myNumber: number = sayHello('oops'); // error 9 | // sayHello('Dan'); // fine 10 | 11 | /* +--------------------Arrow Functions--------------------+ */ 12 | 13 | // const sayGoodbye = (name: string): string => { 14 | // // other lines 15 | // return `Bye ${name}`; 16 | // }; 17 | // sayGoodbye('Dan'); 18 | 19 | // const aStr = 'string'; 20 | // aStr.split('').forEach(char => char.padStart(2, 'X')); 21 | 22 | /* +--------------------Optional Parameters--------------------+ */ 23 | 24 | // function optional(maybe?: string) { 25 | // if (!maybe) return; 26 | // // maybe is definitely a string now 27 | // return maybe.length; 28 | // } 29 | 30 | // optional('anyString'); // fine 31 | // optional(); // also fine 32 | 33 | /* +--------------------Rest Parameters--------------------+ */ 34 | 35 | // function variadic(a: string, ...extra: string[]) { 36 | // // do something with strings 37 | // } 38 | 39 | // variadic('just one string'); // fine 40 | // variadic('one string', 'and', 'any', 'others'); // also fine 41 | 42 | // function variadic(a: string, ...extra: Array) { 43 | // // do something with strings 44 | // } 45 | 46 | /* +--------------------Rest Arguments--------------------+ */ 47 | 48 | // function variadic(a: string, ...extra: string[]): string { 49 | // return a.concat(...extra); 50 | // } 51 | 52 | // function variadic(a: string, ...extra: string[]): string { 53 | // return a.concat(extra[0], extra[1]); 54 | // } 55 | 56 | /* +--------------------Destructured Parameters--------------------+ */ 57 | 58 | //let [height, weight, age] = [180, 70, 44]; 59 | //let { height, weight, age } = { height: 180, weight: 70, age: 44 }; 60 | 61 | // function showlocation({ lat, lng }: { lat: string, lng: string }): void { 62 | // window.open(`https://www.openstreetmap.org/#map=16/${lat}/${lng}`); 63 | // } 64 | // showlocation({ lat: '50.9272', lng: '-1.4015' }); 65 | 66 | // type location = { 67 | // zoom: string; 68 | // lat: string; 69 | // lng: string 70 | // } 71 | // function showLocation2({ zoom, lat, lng }: location): void { 72 | // window.open(`https://www.openstreetmap.org/#map=${zoom}/${lat}/${lng}`) 73 | // } 74 | 75 | // let [height, weight, age] = [180, 70,,]; 76 | 77 | /* +--------------------Void Functions--------------------+ */ 78 | 79 | // function sideEffect(): void { 80 | // // return 'oops'; // error 81 | // } 82 | 83 | /* +--------------------Function Type Expressions--------------------+ */ 84 | 85 | // let numberFn: (...nums: number[]) => number; 86 | // numberFn = (...nums: number[]): number => { 87 | // return nums.reduce((prev: number, curr: number) => prev + curr, 0); 88 | // } 89 | // numberFn(1, 2, 3); // 6 90 | 91 | // numberFn = (...nums: number[]): number => { 92 | // return nums.reduce((prev: number, curr: number) => { 93 | // if (!prev) return curr; 94 | // return prev * curr; 95 | // }); 96 | // } 97 | // numberFn(4, 5, 6); // 120 98 | 99 | // type numberFn2 = (...nums: number[]) => number; 100 | // const addAllTheNums: numberFn2 = (...numbers: number[]) => { 101 | // return numbers.reduce((prev, next) => prev + next, 0); 102 | // } 103 | 104 | /* +--------------------Call Signatures--------------------+ */ 105 | 106 | // type numberFn3 = { 107 | // operation: string; 108 | // (...nums: number[]): number; 109 | // } 110 | 111 | // const myNumberFn3 = ((...nums: number[]): number => { 112 | // return nums.reduce((prev: number, curr: number) => prev + curr, 0); 113 | // }) as numberFn3; 114 | // myNumberFn3.operation = 'Sum'; 115 | // myNumberFn3.oops = 'error!'; 116 | 117 | /* +--------------------Function Type Interfaces--------------------+ */ 118 | 119 | // interface logFn { 120 | // (arg0: string): void; 121 | // } 122 | 123 | // let myLogFn: logFn = (str: string) => console.log(str); 124 | 125 | /* +--------------------This Parameter--------------------+ */ 126 | 127 | // interface user { 128 | // name: string; 129 | // new (name: string): user; 130 | // } 131 | 132 | // function User(this: user, name: string): user { 133 | // this.name = name; 134 | // return this; 135 | // } 136 | // const user1 = new (User as any)('Dan'); 137 | // console.log(user1.name); // Dan 138 | 139 | // const User = function(this: user, name: string) { 140 | // this.name = name 141 | // } as unknown as { new (name: string): user }; 142 | // const user1 = new User('Dan'); 143 | 144 | /* +--------------------Function Overloads--------------------+ */ 145 | 146 | // function fn(a: string): string[]; 147 | // function fn(a: number): string[]; 148 | // function fn(a: string[], sep?: string): string; 149 | // function fn(a: string | number | string[], sep?: string): string | string[] { 150 | // if (typeof a === 'string') { 151 | // return a.split(''); 152 | // } else if (typeof a === 'number') { 153 | // return a.toString().split(''); 154 | // } else { 155 | // return a.join(sep ?? ''); 156 | // } 157 | // } 158 | 159 | // fn('test'); // ['t', 'e', 's', 't'] 160 | // fn(1234); // ['1', '2', '3', '4'] 161 | // fn(['a', 'b', 'c'], '|'); // 'a|b|c' 162 | // // fn(true); // error 163 | 164 | /* +--------------------Overloading Arrow Functions--------------------+ */ 165 | 166 | // type fn2 = { 167 | // (a: string): string[]; 168 | // (a: number): string[]; 169 | // (a: string | number | string[], sep?: string): string | string[]; 170 | // } 171 | 172 | // const ol = ((a: string | number | string[], sep?: string): string | string[] => { 173 | // if (typeof a === 'string') { 174 | // return a.split(''); 175 | // } else if (typeof a === 'number') { 176 | // return a.toString().split(''); 177 | // } else { 178 | // return a.join(sep ?? ''); 179 | // } 180 | // }) as fn2; 181 | 182 | // ol('test'); // ['t', 'e', 's', 't'] 183 | // ol(1234); // ['1', '2', '3', '4'] 184 | // ol(['a', 'b', 'c'], '|'); // 'a|b|c' 185 | 186 | // function fn3(callback: fn2): void { 187 | // callback('test'); 188 | // } 189 | -------------------------------------------------------------------------------- /Chapter 07/generator-functions.ts: -------------------------------------------------------------------------------- 1 | /* +--------------------Generator Functions--------------------+ */ 2 | 3 | function* randomInt(): Generator { 4 | while (true) { 5 | yield Math.round(Math.random() * 10); 6 | } 7 | } 8 | 9 | const randomIterator = randomInt(); 10 | // console.log(randomIterator.next().value).toString(); 11 | 12 | function* randomInt2(limit: number): Generator { 13 | let currentLimit = limit; 14 | while (true) { 15 | if (--currentLimit >= 0) { 16 | yield Math.round(Math.random() * 10); 17 | } else { 18 | return 'Random numbers exhausted!'; 19 | } 20 | } 21 | } 22 | 23 | const randomIterator2 = randomInt2(1); 24 | // console.log(randomIterator2.next().value); 25 | // console.log(randomIterator2.next().value); // Random numbers exhausted! 26 | // console.log(randomIterator2.next().value); // undefined 27 | 28 | function* randomInt3(limit: number): Generator { 29 | let currentLimit = limit; 30 | const message = 'Random numbers exhausted!'; 31 | while (true) { 32 | if (--currentLimit >= 0) { 33 | if (yield Math.round(Math.random() * 10)) { 34 | break; 35 | } 36 | } else { 37 | return message; 38 | } 39 | } 40 | return message; 41 | } 42 | 43 | const randomIterator3 = randomInt3(10); 44 | console.log(randomIterator3.next().value); 45 | console.log(randomIterator3.next().value); 46 | console.log(randomIterator3.next(true).value); // Random numbers exhausted! 47 | console.log(randomIterator3.next().value); // undefined 48 | -------------------------------------------------------------------------------- /Chapter 07/generic-functions.ts: -------------------------------------------------------------------------------- 1 | function echo(input: T): T { 2 | return input; 3 | } 4 | 5 | // let oops: string = echo(123); 6 | let strTest: string = echo('Test'); // Test 7 | let numTest = echo(123); // 123 8 | 9 | interface Item { 10 | save: (arg0: any[]) => void; 11 | contents: any[]; 12 | [key: string]: any; 13 | } 14 | 15 | function save(item: T): boolean | unknown { 16 | try { 17 | item.save(item.contents); 18 | return true; 19 | } catch(err) { 20 | throw new Error('Save failed'); 21 | } 22 | } 23 | 24 | const basket: Item = { 25 | save: () => { 26 | // save wherever... 27 | }, 28 | contents: [], 29 | } 30 | save(basket); // fine 31 | // save('oops'); // Error 32 | -------------------------------------------------------------------------------- /Chapter 08/abstract.ts: -------------------------------------------------------------------------------- 1 | /* +--------------------Abstract Classes--------------------+ */ 2 | 3 | // abstract class Shape { 4 | // constructor(public kind: string, public isEuclidian?: boolean) { } 5 | // } 6 | 7 | // class Square extends Shape { 8 | // constructor(public sidesLength: number) { 9 | // super('square', true); 10 | // } 11 | // } 12 | 13 | // const badShape = new Shape('triangle'); // error 14 | 15 | /* +--------------------Abstract Properties--------------------+ */ 16 | 17 | // abstract class Shape { 18 | // public abstract kind: string; 19 | // constructor(public isEuclidian?: boolean) { } 20 | // } 21 | 22 | // class Square extends Shape { 23 | // constructor(public kind: string, public sidesLength: number) { 24 | // super(true); 25 | // } 26 | // } 27 | 28 | // const mySquare = new Square('square', 3); // fine 29 | 30 | /* +--------------------Abstract Methods--------------------+ */ 31 | 32 | // abstract class Shape { 33 | // constructor(public kind: string, public isEuclidian?: boolean) { } 34 | // abstract getKind(): string; 35 | // } 36 | 37 | // class Square extends Shape { 38 | // constructor(public kind: string, public sidesLength: number) { 39 | // super('square', true); 40 | // } 41 | 42 | // getKind(): string { 43 | // return this.kind; 44 | // } 45 | // } 46 | 47 | /* +--------------------Abstract Construct Signatures--------------------+ */ 48 | 49 | // abstract class Shape { 50 | // constructor(public kind: string, public isEuclidian?: boolean) { } 51 | // } 52 | -------------------------------------------------------------------------------- /Chapter 08/classes.ts: -------------------------------------------------------------------------------- 1 | /* +--------------------Class Declarations--------------------+ */ 2 | 3 | // class Shape { 4 | // kind: string; 5 | 6 | // constructor(kind: string) { 7 | // this.kind = kind; 8 | // } 9 | // } 10 | 11 | /* +--------------------Class Expressions--------------------+ */ 12 | 13 | // const Shape = class { 14 | // kind: string; 15 | 16 | // constructor(kind: string) { 17 | // this.kind = kind; 18 | // } 19 | // } 20 | 21 | /* +--------------------Constructors--------------------+ */ 22 | 23 | /* 1 */ 24 | // const myShape = new Shape('square'); 25 | // myShape.kind; // square 26 | 27 | // class Shape { 28 | // kind = 'square'; 29 | 30 | // constructor(kind: string) { 31 | // } 32 | // } 33 | 34 | // const myShape = new Shape(); // error 35 | 36 | /* 2 */ 37 | // class Shape { 38 | // kind: string; 39 | 40 | // constructor(kind = 'square') { 41 | // this.kind = kind; 42 | // } 43 | // } 44 | 45 | // const myShape = new Shape(); // fine 46 | // const otherShape = new Shape('hexagon'); // also fine 47 | 48 | /* 3 */ 49 | // class Shape { 50 | // kind: string; 51 | // isEuclidian: boolean | undefined; 52 | 53 | // constructor(kind: string, isEuclidian?: boolean) { 54 | // this.kind = kind; 55 | // this.isEuclidian = isEuclidian; 56 | // } 57 | // } 58 | 59 | // const ball = new Shape('sphere', false); // fine 60 | // const wrong = new Shape(123); // error 61 | 62 | /* +--------------------Constructor Overloading--------------------+ */ 63 | 64 | // class Shape { 65 | // kind: string; 66 | // isEuclidian: boolean | undefined; 67 | 68 | // constructor(kind: string); 69 | // constructor(kind: string, isEuclidian: boolean); 70 | // constructor(kind: string, isEuclidian?: boolean) { 71 | // this.kind = kind; 72 | // this.isEuclidian = isEuclidian; 73 | // } 74 | // } 75 | 76 | // const tile = new Shape('square', true); 77 | // const ball = new Shape('sphere'); 78 | 79 | /* +--------------------Access Modifiers--------------------+ */ 80 | 81 | /* 1 */ 82 | 83 | // class Shape { 84 | // public kind: string; 85 | // public isEuclidian: boolean | undefined; 86 | 87 | // constructor(kind: string, isEuclidian?: boolean) { 88 | // this.kind = kind; 89 | // this.isEuclidian = isEuclidian; 90 | // } 91 | // } 92 | 93 | /* 2 */ 94 | 95 | // class Shape { 96 | // public kind: string; 97 | // public isEuclidian: boolean | undefined; 98 | 99 | // private constructor(kind: string, isEuclidian?: boolean) { 100 | // this.kind = kind; 101 | // this.isEuclidian = isEuclidian; 102 | // } 103 | // } 104 | 105 | // const hole = new Shape('circle'); // error 106 | 107 | /* 3 */ 108 | 109 | // class Shape { 110 | // private kind = 'private'; 111 | // } 112 | 113 | // const myShape = new Shape(); 114 | // console.log(myShape.kind); // error 115 | // console.log(myShape['kind']); // private 116 | 117 | /* +--------------------Private Members in JavaScript--------------------+ */ 118 | 119 | // class Shape { 120 | // #kind = 'private'; 121 | // } 122 | 123 | // const myShape = new Shape(); 124 | // console.log(myShape.kind); // error 125 | 126 | /* +--------------------Parameter Properties--------------------+ */ 127 | 128 | // class Shape { 129 | // constructor(public kind: string, public isEuclidian?: boolean) {} 130 | // } 131 | 132 | // const block = new Shape('cube'); 133 | // console.log(block.kind); // cube 134 | 135 | /* +--------------------Getters and Setters--------------------+ */ 136 | 137 | // class Shape { 138 | // private _kind = 'shape'; 139 | // private allowed = ['square', 'rectangle']; 140 | 141 | // get kind(): string { 142 | // return this._kind; 143 | // } 144 | 145 | // set kind(value: string) { 146 | // if (this.allowed.includes(value)) { 147 | // this._kind = value; 148 | // } else { 149 | // console.error('Kind not allowed'); 150 | // } 151 | // } 152 | // } 153 | 154 | // const shape = new Shape(); 155 | // shape.kind = 'circle'; // error 156 | // console.log(shape.kind); // shape 157 | 158 | // shape.kind = 'square'; // allowed 159 | // console.log(shape.kind); // square 160 | 161 | /* +--------------------this Parameters--------------------+ */ 162 | 163 | // class Shape { 164 | // private _kind = 'shape'; 165 | 166 | // getKind(this: Shape): string { 167 | // return this._kind; 168 | // } 169 | // } 170 | 171 | // const myShape = new Shape(); 172 | // console.log(myShape.getKind()); // shape 173 | 174 | // const myObj = { 175 | // getKind: myShape.getKind 176 | // }; 177 | // console.log(myObj.getKind()); // undefined 178 | 179 | /* +--------------------Index Signatures--------------------+ */ 180 | // class Shape { 181 | // [i: number]: string; 182 | 183 | // constructor(public kind: string, public isEuclidian?: boolean) { 184 | // this[0] = crypto.randomUUID(); 185 | // } 186 | // } 187 | 188 | // const plate = new Shape('circle'); 189 | // console.log(plate[0]); // a guid 190 | 191 | /* +--------------------Implementing an Interface--------------------+ */ 192 | 193 | /* 1 */ 194 | 195 | // interface Placeable { 196 | // left: number; 197 | // top: number; 198 | // } 199 | 200 | // class Shape implements Placeable { 201 | // left: number; 202 | // top: number; 203 | // constructor(public kind: string, coords: placeable) { 204 | // this.left = coords.left; 205 | // this.top = coords.top; 206 | // } 207 | // } 208 | 209 | // const tile = new Shape('square', { left: 0, top: 0 }); 210 | 211 | /* 2 */ 212 | 213 | // interface Placeable { 214 | // left: number; 215 | // top: number; 216 | // zIndex?: number; 217 | // changeZIndex?: (newIndex: number) => void; 218 | // } 219 | 220 | /* +--------------------Static Class Members--------------------+ */ 221 | 222 | // class Shape { 223 | // private static shapeInstances: Shape[] = []; 224 | // constructor() { 225 | // Shape.shapeInstances.push(this); 226 | // } 227 | // static getShapes() { 228 | // return Shape.shapeInstances; 229 | // } 230 | // } 231 | 232 | // const myShape = new Shape(); 233 | // const myOtherShape = new Shape(); 234 | 235 | // console.log(Shape.getShapes().length); // 2 236 | 237 | /* +--------------------Static Blocks--------------------+ */ 238 | 239 | // class Shape { 240 | // static allowedKinds: string[]; 241 | // static { 242 | // Shape.allowedKinds = ['square']; 243 | // } 244 | // constructor(public kind: string) { 245 | // if (!Shape.allowedKinds.includes(kind)) { 246 | // throw new Error(`'${kind}' not allowed`); 247 | // } 248 | // this.kind = kind; 249 | // } 250 | // } 251 | 252 | // const shape1 = new Shape('square'); // fine 253 | // const shape2 = new Shape('circle'); // Error: 'circle' not allowed 254 | -------------------------------------------------------------------------------- /Chapter 08/decorators.ts: -------------------------------------------------------------------------------- 1 | /* +--------------------Class Decorators--------------------+ */ 2 | 3 | /* 1 */ 4 | 5 | // function Decorated(target: typeof Shape) { 6 | // return class extends target { 7 | // kind = 'decorated-shape' 8 | // } 9 | // } 10 | 11 | // @Decorated 12 | // class Shape {} 13 | 14 | // const myShape = new Shape(); 15 | // (myShape as any).kind; // decorated-shape 16 | 17 | /* 2 */ 18 | 19 | // function Decorated(target: typeof Shape) { 20 | // return class extends target { 21 | // kind = 'decorated-shape' 22 | // } 23 | // } 24 | 25 | // function DecoratedAgain(target: typeof Shape) { 26 | // return class extends target { 27 | // special = 'decorated again!' 28 | // } 29 | // } 30 | 31 | // @Decorated 32 | // @DecoratedAgain 33 | // class Shape {} 34 | 35 | // const myShape = new Shape(); 36 | // (myShape as any).special; // decorated again! 37 | 38 | -------------------------------------------------------------------------------- /Chapter 08/design-patterns.ts: -------------------------------------------------------------------------------- 1 | /* +--------------------TypeScript Design Patterns--------------------+ */ 2 | 3 | abstract class Observable { 4 | constructor(public value: T) {} 5 | private observers: ((update: T) => void)[] = []; 6 | observe(observer: (update: T) => void) { 7 | this.observers.push(observer); 8 | } 9 | notify() { 10 | this.observers.forEach((observer) => observer(this.value)); 11 | } 12 | } 13 | 14 | class ObservableString extends Observable { 15 | updateValue(value: string) { 16 | this.value = value; 17 | this.notify(); 18 | } 19 | } 20 | 21 | const myObservableString = new ObservableString('test'); 22 | myObservableString.observe((update: string) => { 23 | console.log(`The value was updated to ${update}`); 24 | }); 25 | 26 | myObservableString.updateValue('new value!'); // The value was updated to new value! 27 | -------------------------------------------------------------------------------- /Chapter 08/generic.ts: -------------------------------------------------------------------------------- 1 | /* +--------------------Generic Classes--------------------+ */ 2 | 3 | // class Shape { 4 | // constructor(public kind: T) {} 5 | // } 6 | 7 | // class Square { 8 | // constructor(public sidesLength: number) {} 9 | // } 10 | 11 | // class Triangle { 12 | // constructor( 13 | // public sidesLength: [number, number, number], 14 | // public angles: [number, number, number] 15 | // ) {} 16 | // } 17 | 18 | // const mySquare = new Square(3); 19 | // const myTriangle = new Triangle([2, 2, 2], [60, 60, 60]); 20 | // const myShape = new Shape(mySquare); 21 | // const otherShape = new Shape(myTriangle); 22 | // console.log(myShape.kind); // Square 23 | // console.log(otherShape.kind); // Triangle 24 | // const allShapes: Shape[] = [myShape, otherShape]; 25 | // const allSquares: Shape[] = [myShape]; 26 | -------------------------------------------------------------------------------- /Chapter 08/inheritance.ts: -------------------------------------------------------------------------------- 1 | /* +--------------------Inheritance--------------------+ */ 2 | 3 | /* 1 */ 4 | 5 | // class Shape { 6 | // constructor(public kind: string, public isEuclidian?: boolean) { } 7 | // } 8 | 9 | // class Square extends Shape { 10 | // constructor(public sidesLength: number) { 11 | // super('square', true); 12 | // } 13 | 14 | // getArea(): number { 15 | // return this.sidesLength ** 2; 16 | // } 17 | // } 18 | 19 | // const myShape = new Square(3); 20 | // console.log(myShape.getArea()); // 9 21 | // console.log(myShape.kind); // square 22 | 23 | /* 2 */ 24 | 25 | // class Shape { 26 | // constructor(public kind: string, public isEuclidian?: boolean) { } 27 | 28 | // logKind(): void { 29 | // console.log('Base kind: ', this.kind); 30 | // } 31 | // } 32 | 33 | // class Square extends Shape { 34 | // logKind(): void { 35 | // console.log('Derived kind: ', this.kind); 36 | // } 37 | // } 38 | 39 | // const generic = new Shape('shape'); 40 | // const box = new Shape('cuboid'); 41 | 42 | // generic.logKind(); // Base kind: shape 43 | // box.logKind(); // Derived kind: cuboid 44 | -------------------------------------------------------------------------------- /Chapter 09/narrowing.ts: -------------------------------------------------------------------------------- 1 | /* +--------------------Narrowing--------------------+ */ 2 | 3 | // const which: string | number = 'str'; 4 | // which.concat('a'); 5 | 6 | // const num1 = 1; 7 | // let num2 = 2; 8 | 9 | /* +--------------------Truthy Type Guards--------------------+ */ 10 | 11 | // type Wrapper = { 12 | // contents?: string; 13 | // } 14 | 15 | // const myWrapper: Wrapper = {}; 16 | // ''.concat(myWrapper.contents); // Error 17 | // if (myWrapper.contents) { 18 | // ''.concat(myWrapper.contents); // Fine 19 | // } 20 | 21 | /* +--------------------Narrowing With typeof--------------------+ */ 22 | 23 | // function getIdPrefix(id: string | number) { 24 | // // if (typeof id === 'string') { 25 | // // return id.slice(0, 3); 26 | // // } else { 27 | // // return id.toString().slice(0, 3); 28 | // // } 29 | // switch (typeof id) { 30 | // case 'string': 31 | // return id.slice(0, 3); 32 | // case 'number': 33 | // return id.toString().slice(0, 3); 34 | // } 35 | // } 36 | 37 | /* +--------------------Handling null Values--------------------+ */ 38 | 39 | // type Rocket = { launch: () => void }; 40 | // function attemptLaunch(rocket: Rocket | null) { 41 | // if (rocket !== null) { 42 | // rocket.launch(); 43 | // } 44 | // } 45 | 46 | /* +--------------------Narrowing With instanceof--------------------+ */ 47 | 48 | // class Employee { 49 | // work = () => console.log('Working'); 50 | // } 51 | 52 | // class Manager { 53 | // manage = () => console.log('Managing'); 54 | // } 55 | 56 | // function work(emp: Employee | Manager) { 57 | // if (emp instanceof Employee) { 58 | // emp.work(); 59 | // } else { 60 | // emp.manage(); 61 | // } 62 | // } 63 | 64 | /* +--------------------Narrowing With the in Operator--------------------+ */ 65 | 66 | // type SaveLocally = { 67 | // saveInLocalStorage: () => boolean; 68 | // }; 69 | // type SaveRemotely = { 70 | // sendToApi: () => boolean; 71 | // }; 72 | 73 | // function save(item: SaveLocally | SaveRemotely): boolean { 74 | // if ('saveInLocalStorage' in item) { 75 | // return item.saveInLocalStorage(); 76 | // } else { 77 | // return item.sendToApi(); 78 | // } 79 | // } 80 | 81 | /* +--------------------Narrowing With Type Predicates--------------------+ */ 82 | 83 | // type MachineKind = 'robot' | 'drone'; 84 | // interface Machine { kind: MachineKind; } 85 | 86 | // class Humanoid implements Machine { 87 | // kind: MachineKind = 'robot'; 88 | // walk() { /* do walking */ } 89 | // } 90 | 91 | // function machineIsHumanoid(machine: Machine): machine is Humanoid { 92 | // // return machine.kind === 'robot'; 93 | // return 'walk' in machine; 94 | // } 95 | 96 | // const terminator: Machine = new Humanoid(); 97 | // // terminator.walk(); 98 | 99 | // if (machineIsHumanoid(terminator)) { 100 | // terminator.walk(); 101 | // } 102 | 103 | /* +--------------------Discriminated Unions--------------------+ */ 104 | 105 | // interface Car { 106 | // drive: () => void; 107 | // kind: 'car'; 108 | // } 109 | // interface Truck { 110 | // haul: () => void; 111 | // kind: 'truck'; 112 | // } 113 | // interface Plane { 114 | // fly: () => void; 115 | // kind: 'plane'; 116 | // } 117 | // type Vehicle = Car | Truck | Plane; 118 | 119 | // const myJet: Vehicle = { 120 | // kind: 'plane', 121 | // fly: () => { /* do flying */ } 122 | // }; 123 | // myJet.fly(); 124 | 125 | /* +--------------------Assertion Functions--------------------+ */ 126 | 127 | /* 1 */ 128 | 129 | // type User = { name: string; getName?: () => string; } 130 | // type Artifact = { id: string, getId?: () => string; } 131 | 132 | // const mightBeUser: User | Artifact = { 133 | // name: 'test', 134 | // getName: () => 'test', 135 | // }; 136 | 137 | // function assertIsUser(test: any): asserts test { 138 | // if (!test) throw new Error('Not a valid user'); 139 | // } 140 | 141 | // assertIsUser(mightBeUser.getName !== undefined); 142 | 143 | /* 2 */ 144 | 145 | // type User = { name: string; getName: () => string; } 146 | // type Artifact = { id: string, getId: () => string; } 147 | 148 | // function assertIsUser(maybeUser: any): asserts maybeUser is User { 149 | // if (maybeUser.name === undefined || maybeUser.getName === undefined) { 150 | // throw new Error('Not a valid user'); 151 | // } 152 | // } 153 | 154 | // function getName(obj: User | Artifact) { 155 | // assertIsUser(obj); 156 | // return obj.getName(); 157 | // } 158 | 159 | /* +--------------------Using as const--------------------+ */ 160 | 161 | /* 1 */ 162 | 163 | // let num2 = 2 as const; 164 | // num2 = 3; // error 165 | 166 | /* 2 */ 167 | 168 | // const mutable = []; 169 | // mutable.push('yay'); // Fine 170 | 171 | // const immutable = ['constant'] as const; 172 | // immutable[0]; // constant 173 | // immutable.push('nope'); // Error 174 | // immutable[0] = 'nope'; // Error 175 | 176 | /* 3 */ 177 | 178 | // const mutableObj = { test: 'anything' }; 179 | // mutableObj.test = 'something else'; // fine 180 | 181 | // const immutableObj = { test: 'anything' } as const; 182 | // immutableObj.test = 'something else'; // error 183 | 184 | 185 | 186 | 187 | -------------------------------------------------------------------------------- /Chapter 10/manipulating-types.ts: -------------------------------------------------------------------------------- 1 | /* +--------------------Generics--------------------+ */ 2 | 3 | /* 1 */ 4 | 5 | // interface Identifier { 6 | // value: Id; 7 | // } 8 | 9 | // const stringId: Identifier = { value: 'abc123' }; 10 | // const numberId: Identifier = { value: 123 }; 11 | // const symbolId: Identifier = { value: Symbol() }; 12 | 13 | /* 2 */ 14 | 15 | // type Identifier = { value: Id }; 16 | 17 | // const stringId: Identifier = { value: 'abc123' }; 18 | // const numberId: Identifier = { value: 123 }; 19 | // const symbolId: Identifier = { value: Symbol() }; 20 | 21 | /* 3 */ 22 | 23 | // class Shape { 24 | // constructor(public kind: Kind) {} 25 | // } 26 | 27 | // class Square { 28 | // constructor(public sidesLength: number) {} 29 | // } 30 | // const myShape: Shape = new Shape(new Square(5)); 31 | // console.log(myShape.kind); // Square 32 | // console.log(myShape.kind.sidesLength); // 5 33 | 34 | /* 4 */ 35 | 36 | // type Email = { 37 | // to: string; 38 | // }; 39 | // type Sms = { 40 | // to: number; 41 | // }; 42 | 43 | // function send(item: T): T { 44 | // if ('to' in item && typeof item.to === 'string') { 45 | // // send email 46 | // } else if ('to' in item && typeof item.to === 'number') { 47 | // // send sms 48 | // } 49 | // return item; 50 | // } 51 | 52 | // const msg: Email = { to: 'someone@somwhere' }; 53 | // send(msg); 54 | 55 | /* +--------------------Conditional Types--------------------+ */ 56 | 57 | // interface BaseType { a: string }; 58 | // interface SubType extends BaseType { b: number }; 59 | // interface OtherType { c: boolean }; 60 | 61 | // type conditionalType = SubType extends BaseType 62 | // ? string 63 | // : number; // True, string 64 | 65 | // type conditionalType = OtherType extends BaseType 66 | // ? string 67 | // : number; // False, number 68 | 69 | /* +--------------------Indexed Access Types--------------------+ */ 70 | 71 | // type User = { 72 | // id: string | number; 73 | // email: string; 74 | // }; 75 | // type Id = User['id']; 76 | // type IdOrEmail = User['id'|'email']; 77 | 78 | // type Email = { 79 | // subject: string; 80 | // attachment: { 81 | // filename: string; 82 | // size: number; 83 | // }; 84 | // }; 85 | // type AttachmentSize = Email['attachment']['size']; 86 | 87 | /* +--------------------Mapped Types--------------------+ */ 88 | 89 | /* 1 */ 90 | 91 | // type FalseType = { 92 | // a: false; 93 | // b: false; 94 | // }; 95 | // type TrueType = { 96 | // [P in keyof T]: true; 97 | // }; 98 | // type NewType = TrueType; 99 | 100 | /* 2 */ 101 | 102 | // type MakeReadonly = { 103 | // +readonly [P in keyof T]: T[P]; 104 | // }; 105 | // type WritableType = { 106 | // [key: string]: string; 107 | // }; 108 | // type NonWritableType = MakeReadonly; 109 | 110 | /* 3 */ 111 | 112 | // type CompletelyOptional = { 113 | // a?: string; 114 | // b?: number; 115 | // }; 116 | // type NotOptional = { 117 | // [P in keyof T]-?: T[P]; 118 | // }; 119 | // type Mandatory = NotOptional; 120 | 121 | /* 4 */ 122 | 123 | // type NamedProps = { 124 | // a: string; 125 | // b: number; 126 | // }; 127 | 128 | // type AnyProps = { 129 | // [P in keyof T as string]: T[P]; 130 | // }; 131 | 132 | // const rigid: NamedProps = { a: '', b: 1, c: 'oops' }; 133 | // type Flexible = AnyProps; 134 | // const flexible: Flexible = { a: '', b: 1, c: 'works' }; 135 | -------------------------------------------------------------------------------- /Chapter 10/template-literal-types.ts: -------------------------------------------------------------------------------- 1 | /* +--------------------Template Literal Types--------------------+ */ 2 | 3 | // type Template = 'template'; 4 | // type Literal = `${Template} literal`; 5 | 6 | // type versions = '1.0' | '1.1' | '1.2'; 7 | // type names = 'alpha' | 'beta' | 'gold'; 8 | // type releases = `${versions}-${names}`; 9 | 10 | /* +--------------------Capitalize--------------------+ */ 11 | 12 | // type input = 'input'; 13 | // type result = Capitalize; 14 | 15 | /* +--------------------Lowercase--------------------+ */ 16 | 17 | // type input = 'INPUT'; 18 | // type result = Lowercase; 19 | 20 | /* +--------------------Uncapitalize--------------------+ */ 21 | 22 | // type input = 'Input'; 23 | // type result = Uncapitalize; 24 | 25 | /* +--------------------Uppercase--------------------+ */ 26 | 27 | // type input = 'input'; 28 | // type result = Uppercase; 29 | -------------------------------------------------------------------------------- /Chapter 10/utilities.ts: -------------------------------------------------------------------------------- 1 | /* +--------------------Awaited--------------------+ */ 2 | 3 | // type ApiResponse = { 4 | // data: string; 5 | // } 6 | // type unwrappedApiResponse = Awaited>; 7 | 8 | // async function getApiResponse(): Promise { 9 | // return { data: 'test'}; 10 | // } 11 | 12 | // type RespType = Awaited>; 13 | 14 | /* +--------------------ConstructorParameters--------------------+ */ 15 | 16 | // type ConstructorParams = ConstructorParameters; 17 | 18 | // class Car { 19 | // constructor(public model: string) {} 20 | // } 21 | // type CarConstructorParams = ConstructorParameters; 22 | 23 | /* +--------------------Exclude--------------------+ */ 24 | 25 | // type AllMembers = string | number | boolean; 26 | // type ExcludeMembers = string | number; 27 | // type RemainingMembers = Exclude; 28 | 29 | /* +--------------------Extract--------------------+ */ 30 | 31 | // type AllMembers = string | number | boolean; 32 | // type ExtractMembers = string | number; 33 | // type RemainingMembers = Extract; 34 | 35 | /* +--------------------InstanceType--------------------+ */ 36 | 37 | // class Cat { 38 | // constructor(public name: string) {} 39 | // meow() { console.log('Meow') } 40 | // } 41 | // class Dog { 42 | // constructor(public name: string) {} 43 | // bark() { console.log('Woof') } 44 | // } 45 | 46 | // function PetFactory any>(pet: T, n: string): InstanceType { 47 | // return new pet(n); 48 | // } 49 | 50 | // const kitty = PetFactory(Cat, 'fluffy'); 51 | // console.log(kitty.name); // fluffy 52 | // kitty.meow(); // error without InstanceType 53 | 54 | /* +--------------------NonNullable--------------------+ */ 55 | 56 | // type CannotBeNull = NonNullable 57 | 58 | // type NotNull = {}; 59 | // const notNull1: NotNull = 1; // Fine 60 | // const notNull2: NotNull = 'Not empty!'; // Fine 61 | // const notNull3: NotNull = true; // Fine 62 | // const notNull4: NotNull = ['anything']; // Fine 63 | // const notNull5: NotNull = { a: null }; // Fine 64 | 65 | // const notNull6: NotNull = null; // Error 66 | // const notNull7: NotNull = undefined; // Error 67 | 68 | // type CannotBeNull2 = (string | null | undefined) & {} 69 | 70 | // type HasNull = { a: string | null } 71 | // type CanContainNull = NonNullable 72 | // const someObj: CanContainNull = { 73 | // a: null, 74 | // } 75 | 76 | /* +--------------------Omit--------------------+ */ 77 | 78 | // interface Operations { 79 | // create: () => void; 80 | // read: () => void; 81 | // update: () => void; 82 | // delete: () => void; 83 | // } 84 | // type WriteOps = 'create' | 'update' | 'delete' 85 | // type ReadOnlyOps = Omit; 86 | // const read: ReadOnlyOps = { 87 | // read: () => undefined, 88 | // } 89 | 90 | /* +--------------------OmitThisParameter--------------------+ */ 91 | 92 | // type User = { 93 | // name: string; 94 | // }; 95 | // function getUserName(this: User): string { 96 | // return this.name; 97 | // } 98 | // const userName = getUserName.call({ name: 'Bill' }); 99 | 100 | // const fnWithoutThisParam: OmitThisParameter = getUserName.bind({ name: 'Fred' }); 101 | 102 | /* +--------------------Partial--------------------+ */ 103 | 104 | // type Mandatory = { 105 | // required: string; 106 | // } 107 | // const mustHaveRequired: Mandatory = { 108 | // required: '', 109 | // } 110 | // type NonMandatory = Partial; 111 | // const mightHaveRequired: NonMandatory = {}; 112 | 113 | /* +--------------------Parameters--------------------+ */ 114 | 115 | // type FnParams = Parameters<(a: string, b: number) => void>; 116 | // function myFn(a: boolean, b: string): void {}; 117 | // type FnParams2 = Parameters 118 | 119 | /* +--------------------Pick--------------------+ */ 120 | 121 | // interface Operations { 122 | // create: () => void; 123 | // read: () => void; 124 | // update: () => void; 125 | // delete: () => void; 126 | // } 127 | // type WriteOps = 'create' | 'update' | 'delete'; 128 | // type Write = Pick; 129 | // const write: Write = { 130 | // create: () => undefined, 131 | // update: () => { undefined }, 132 | // delete: () => { undefined }, 133 | // } 134 | 135 | /* +--------------------Readonly--------------------+ */ 136 | 137 | // type Writable = { 138 | // canBeWritten: string; 139 | // } 140 | // const notReadonly: Writable = { canBeWritten: 'yes' }; 141 | // notReadonly.canBeWritten = 'new!'; 142 | 143 | // type NotWritable = Readonly; 144 | // const readonly: NotWritable = { canBeWritten: 'no' }; 145 | //readonly.canBeWritten = 'nope'; // Error 146 | 147 | /* +--------------------Record--------------------+ */ 148 | 149 | // interface Person { age: number }; 150 | // type Names = 'roger'|'dave'; 151 | // type Guitarists = Record; 152 | 153 | // const pf: Guitarists = { 154 | // roger: { age: 79 }, 155 | // dave: { age: 76 }, 156 | // }; 157 | 158 | // type Example = Record; 159 | // const ex: Example = { a: 1 }; 160 | // type Example2 = { [key: string]: number }; 161 | 162 | /* +--------------------Required--------------------+ */ 163 | 164 | // type NonMandatory = { 165 | // optional?: string; 166 | // } 167 | // const mightHaveOptional: NonMandatory = {}; 168 | // type Mandatory = Required; 169 | 170 | // const mustHaveOptional: Mandatory = { 171 | // optional: '', 172 | // }; 173 | 174 | /* +--------------------ReturnType--------------------+ */ 175 | 176 | // type fnReturn = ReturnType<() => boolean>; 177 | 178 | // function myFn(arg0: string): number { return 1; } 179 | // type alsoFnReturn = ReturnType; 180 | 181 | /* +--------------------ThisParameterType--------------------+ */ 182 | 183 | // type User = { 184 | // name: string; 185 | // }; 186 | // function getUserName(this: User): string { 187 | // return this.name; 188 | // } 189 | 190 | // type fnThisType = ThisParameterType; 191 | 192 | /* +--------------------ThisType--------------------+ */ 193 | 194 | // type libCore = { 195 | // version: '1.2' 196 | // }; 197 | // type lib = { 198 | // helperFn: Function 199 | // } & ThisType 200 | 201 | // const myLib: lib = { 202 | // helperFn: function() { return this.version } 203 | // } 204 | -------------------------------------------------------------------------------- /Chapter 11/components/forms/search-field.ts: -------------------------------------------------------------------------------- 1 | export interface SearchField { 2 | value: string; 3 | } 4 | -------------------------------------------------------------------------------- /Chapter 11/components/menus/main-menu.ts: -------------------------------------------------------------------------------- 1 | // import { SearchField } from './search-field'; 2 | 3 | // type mainMenu = { 4 | // searchField?: SearchField; 5 | // } 6 | 7 | import { SearchField } from '../forms/search-field'; 8 | -------------------------------------------------------------------------------- /Chapter 11/config.json: -------------------------------------------------------------------------------- 1 | { 2 | "enableFeature": true 3 | } 4 | -------------------------------------------------------------------------------- /Chapter 11/customer-social.ts: -------------------------------------------------------------------------------- 1 | import { Customer } from './customer'; 2 | 3 | declare module './customer' { 4 | interface Customer { 5 | socialMediaName: string; 6 | updateSocialMediaName(name: string): void; 7 | } 8 | } 9 | 10 | const mySocialCustomer: Customer = new Customer('Jon Doe'); 11 | mySocialCustomer.socialMediaName = '@jondoe'; 12 | 13 | Customer.prototype.updateSocialMediaName = function (name: string) { 14 | this.socialMediaName = name; 15 | } 16 | -------------------------------------------------------------------------------- /Chapter 11/customer.ts: -------------------------------------------------------------------------------- 1 | export class Customer { 2 | constructor(public name: string) {} 3 | 4 | public updateName(name: string): void { 5 | this.name = name; 6 | } 7 | } 8 | -------------------------------------------------------------------------------- /Chapter 11/declarations/external/lib1.d.ts: -------------------------------------------------------------------------------- 1 | declare interface Test { 2 | questions: string[]; 3 | } 4 | -------------------------------------------------------------------------------- /Chapter 11/declarations/internal/lib2.d.ts: -------------------------------------------------------------------------------- 1 | /// 2 | 3 | const myTest: Test = { 4 | questions: [] 5 | } 6 | -------------------------------------------------------------------------------- /Chapter 11/declarations/lib3.ts: -------------------------------------------------------------------------------- 1 | interface Examiner { 2 | name: string; 3 | } 4 | 5 | interface Banana { peeled: boolean }; 6 | -------------------------------------------------------------------------------- /Chapter 11/declarations/lib4.ts: -------------------------------------------------------------------------------- 1 | const mrExaminer: Examiner = { 2 | name: '', 3 | } 4 | -------------------------------------------------------------------------------- /Chapter 11/lib/api/api-a.ts: -------------------------------------------------------------------------------- 1 | // import { UiA } from '../ui/ui-a'; 2 | // import { UiB } from '../ui/ui-b'; 3 | 4 | import { UiA, UiB } from "../ui"; 5 | 6 | export interface Api { 7 | apiA: string; 8 | } 9 | -------------------------------------------------------------------------------- /Chapter 11/lib/api/index.ts: -------------------------------------------------------------------------------- 1 | export * from './api-a'; 2 | -------------------------------------------------------------------------------- /Chapter 11/lib/index.ts: -------------------------------------------------------------------------------- 1 | export * from './api'; 2 | export * from './ui'; 3 | -------------------------------------------------------------------------------- /Chapter 11/lib/my-lib.ts: -------------------------------------------------------------------------------- 1 | import { UiA, UiB, Api } from "./"; 2 | -------------------------------------------------------------------------------- /Chapter 11/lib/ui/index.ts: -------------------------------------------------------------------------------- 1 | export * from './ui-a'; 2 | export * from './ui-b'; 3 | 4 | export { UiB } from './ui-b'; 5 | -------------------------------------------------------------------------------- /Chapter 11/lib/ui/ui-a.ts: -------------------------------------------------------------------------------- 1 | export interface UiA { 2 | aProp: string; 3 | } 4 | -------------------------------------------------------------------------------- /Chapter 11/lib/ui/ui-b.ts: -------------------------------------------------------------------------------- 1 | export interface UiB { 2 | bProp: number; 3 | } 4 | -------------------------------------------------------------------------------- /Chapter 11/module1.ts: -------------------------------------------------------------------------------- 1 | /* +--------------------Modules--------------------+ */ 2 | 3 | /* 1 */ 4 | export type Answer = { 5 | score: number; 6 | correct: boolean; 7 | }; 8 | 9 | /* 2 */ 10 | 11 | export interface Exam { 12 | questions: string[]; 13 | answers: Answer[]; 14 | } 15 | 16 | /* 3 */ 17 | 18 | export class MathExam implements Exam { 19 | questions: string[] = []; 20 | answers: Answer[] = []; 21 | } 22 | 23 | export function getScore(exam: MathExam): number { 24 | return exam.answers.reduce((prev, curr) => { 25 | return (curr.correct) ? prev + curr.score : 0; 26 | }, 0); 27 | } 28 | 29 | export const stduent1Score = getScore({ 30 | questions: [], 31 | answers: [], 32 | }); 33 | 34 | /* +--------------------Resolve JSON Module--------------------+ */ 35 | 36 | // import config from './config.json'; 37 | // const enable = config.enableFeature; // fine 38 | -------------------------------------------------------------------------------- /Chapter 11/module2.ts: -------------------------------------------------------------------------------- 1 | /* +--------------------Modules--------------------+ */ 2 | 3 | // import { Exam } from './module1'; 4 | 5 | /* +--------------------Type-only Imports/Exports--------------------+ */ 6 | 7 | // import type { Exam } from './module1'; 8 | // export type { Exam } 9 | 10 | /* +--------------------Base URL--------------------+ */ 11 | 12 | // import { Exam } from 'module1'; 13 | 14 | /* +--------------------Paths--------------------+ */ 15 | 16 | // import { Exam } from 'a'; 17 | 18 | -------------------------------------------------------------------------------- /Chapter 12/definitely-typed/str/index.d.ts: -------------------------------------------------------------------------------- 1 | // Type definitions for str 1.0 2 | // Project: https://github.com/danwellman/str 3 | // Definitions by: Dan Wellman 4 | // Definitions: https://github.com/DefinitelyTyped/DefinitelyTyped 5 | 6 | export const version: '1.0.0'; 7 | export function lastChar(word: string): string; 8 | export function ordinalize(dayOfMonth: string | number): string; 9 | -------------------------------------------------------------------------------- /Chapter 12/definitely-typed/str/str-tests.ts: -------------------------------------------------------------------------------- 1 | import str = require('str'); 2 | 3 | str.lastChar('test'); // $ExpectType string 4 | str.ordinalize('2'); // $ExpectType string 5 | str.ordinalize(3); // $ExpectType string 6 | -------------------------------------------------------------------------------- /Chapter 12/definitely-typed/str/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "module": "commonjs", 4 | "strict": true, 5 | "baseUrl": "../", 6 | "typeRoots": [ 7 | "../" 8 | ], 9 | "types": [], 10 | "noEmit": true, 11 | "forceConsistentCasingInFileNames": true 12 | }, 13 | "files": [ 14 | "index.d.ts", 15 | "str-tests.ts" 16 | ] 17 | } 18 | -------------------------------------------------------------------------------- /Chapter 12/definitely-typed/str/tslint.json: -------------------------------------------------------------------------------- 1 | { "extends": "@definitelytyped/dtslint/dt.json" } 2 | -------------------------------------------------------------------------------- /Chapter 12/global/global.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Global 5 | 6 | 7 | 8 | 11 | 12 | 13 | -------------------------------------------------------------------------------- /Chapter 12/global/my-global-app.ts: -------------------------------------------------------------------------------- 1 | const ordinalized = str.ordinalize(2); 2 | // str.version = '2.0.0'; 3 | 4 | window.getGlobalMessage(); // Hello from the global object 5 | 6 | 'test'.reverse(); // tset 7 | -------------------------------------------------------------------------------- /Chapter 12/global/str-global.d.ts: -------------------------------------------------------------------------------- 1 | /* +--------------------Declaring Global Libraries--------------------+ */ 2 | 3 | /** 4 | * Simple string utils 5 | * @license MIT 6 | */ 7 | declare namespace str { 8 | const version: '1.0.0'; 9 | // let version: '1.0.0'; 10 | 11 | /** 12 | * lastChar 13 | * @param word {string} - The word to get the last character of 14 | * @returns string 15 | */ 16 | function lastChar(word: string): string; 17 | 18 | /** 19 | * ordinalize 20 | * @param {number|string} dayOfMonth - the day of the month to ordinalize 21 | * @returns {string} 22 | */ 23 | function ordinalize(dayOfMonth: number | string): string; 24 | } 25 | 26 | /* +--------------------Declaring Global Functions and Variables--------------------+ */ 27 | 28 | declare const globalMessage: string; 29 | declare function getGlobalMessage(): string; 30 | 31 | /* +--------------------Augmenting Built-ins--------------------+ */ 32 | 33 | interface String { 34 | reverse(this: string): string; 35 | } 36 | -------------------------------------------------------------------------------- /Chapter 12/global/str-global.js: -------------------------------------------------------------------------------- 1 | (function () { 2 | if (window && !window.str) { 3 | window.str = { 4 | version: '1.0.0', 5 | lastChar: function(word) { 6 | return word.slice(-1); 7 | }, 8 | ordinalize: function(dayOfMonth) { 9 | const d = '' + dayOfMonth; 10 | 11 | if (!d) return ''; 12 | if (!parseInt(d, 10)) return ''; 13 | 14 | return str.lastChar(d) === '1' 15 | ? `${dayOfMonth}st` 16 | : str.lastChar(d) === '2' 17 | ? `${dayOfMonth}nd` 18 | : str.lastChar(d) === '3' 19 | ? `${dayOfMonth}rd` 20 | : `${dayOfMonth}th`; 21 | }, 22 | }; 23 | } 24 | }()); 25 | 26 | (function () { 27 | window.globalMessage = 'Hello from the global object'; 28 | window.getGlobalMessage = function() { 29 | return window.globalMessage; 30 | } 31 | }()); 32 | 33 | (function () { 34 | if (!String.prototype.reverse) { 35 | String.prototype.reverse = function() { 36 | return this.split('').reverse().join(''); 37 | } 38 | } 39 | }()); 40 | -------------------------------------------------------------------------------- /Chapter 12/module/my-commonjs-app.ts: -------------------------------------------------------------------------------- 1 | // import str = require('./str-commonjs'); 2 | // str.version; // 1.0.0 3 | -------------------------------------------------------------------------------- /Chapter 12/module/my-module-app.ts: -------------------------------------------------------------------------------- 1 | import { str } from './str-module'; 2 | import reverse from './str-module'; 3 | import { Word } from './str-module'; 4 | 5 | str.lastChar('module'); // e 6 | 7 | reverse('test'); // tset 8 | 9 | const word = new Word(); 10 | const wordcount = word.count('Lorem ipsum'); // 2 11 | -------------------------------------------------------------------------------- /Chapter 12/module/my-umd-app.ts: -------------------------------------------------------------------------------- 1 | import { str } from './str-umd'; 2 | 3 | str.lastChar('umd'); // d 4 | window.str.lastChar('also works!') // ! 5 | -------------------------------------------------------------------------------- /Chapter 12/module/str-commonjs.d.ts: -------------------------------------------------------------------------------- 1 | /* +--------------------Declaring CommonJS Modules--------------------+ */ 2 | 3 | export const version: '1.0.0'; 4 | export function lastChar(word: string): string; 5 | export function ordinalize(dayOfMonth: string | number): string; 6 | -------------------------------------------------------------------------------- /Chapter 12/module/str-commonjs.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | version: '1.0.0', 3 | lastChar: function(word) { 4 | return word.slice(-1); 5 | }, 6 | ordinalize: function(dayOfMonth) { 7 | const d = '' + dayOfMonth; 8 | 9 | if (!d) return ''; 10 | if (!parseInt(d, 10)) return ''; 11 | 12 | return str.lastChar(d) === '1' 13 | ? `${dayOfMonth}st` 14 | : str.lastChar(d) === '2' 15 | ? `${dayOfMonth}nd` 16 | : str.lastChar(d) === '3' 17 | ? `${dayOfMonth}rd` 18 | : `${dayOfMonth}th`; 19 | }, 20 | }; 21 | -------------------------------------------------------------------------------- /Chapter 12/module/str-module.d.ts: -------------------------------------------------------------------------------- 1 | /* +--------------------Declaring Modular Libraries--------------------+ */ 2 | 3 | /** 4 | * Simple modular string utils 5 | * @license MIT 6 | */ 7 | export declare namespace str { 8 | const version: '1.0.0'; 9 | function lastChar(word: string): string; 10 | function ordinalize(dayOfMonth: number | string): string; 11 | } 12 | 13 | /* +--------------------Declaring Default Exports--------------------+ */ 14 | 15 | export default function reverse(word: string): string; 16 | 17 | /* +--------------------Declaring Classes--------------------+ */ 18 | 19 | export declare class Word { 20 | count(sentence: string): number; 21 | } 22 | -------------------------------------------------------------------------------- /Chapter 12/module/str-module.js: -------------------------------------------------------------------------------- 1 | export const str = { 2 | version: '1.0.0', 3 | lastChar: function(word) { 4 | return word.slice(-1); 5 | }, 6 | ordinalize: function(dayOfMonth) { 7 | const d = '' + dayOfMonth; 8 | 9 | if (!d) return ''; 10 | if (!parseInt(d, 10)) return ''; 11 | 12 | return str.lastChar(d) === '1' 13 | ? `${dayOfMonth}st` 14 | : str.lastChar(d) === '2' 15 | ? `${dayOfMonth}nd` 16 | : str.lastChar(d) === '3' 17 | ? `${dayOfMonth}rd` 18 | : `${dayOfMonth}th`; 19 | }, 20 | }; 21 | 22 | export default reverse = function(word) { 23 | if (!word) return ''; 24 | return word.split('').reverse().join(''); 25 | } 26 | 27 | export class Word { 28 | count(sentence) { 29 | return sentence.split(' ').length; 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /Chapter 12/module/str-umd.d.ts: -------------------------------------------------------------------------------- 1 | /* +--------------------Declaring UMD Libraries--------------------+ */ 2 | 3 | /** 4 | * Simple UMD string utils 5 | * @license MIT 6 | */ 7 | export declare namespace str { 8 | const version: '1.0.0'; 9 | function lastChar(word: string): string; 10 | function ordinalize(date: number | string): string; 11 | } 12 | -------------------------------------------------------------------------------- /Chapter 12/module/str-umd.js: -------------------------------------------------------------------------------- 1 | (function (root, factory) { 2 | if (typeof define === 'function' && define.amd) { 3 | define([], factory); 4 | } else if (typeof module === 'object' && module.exports) { 5 | module.exports = factory(); 6 | } else { 7 | root.str = factory(); 8 | } 9 | }(typeof self !== 'undefined' ? self : this, function () { 10 | return { 11 | version: '1.0.0', 12 | lastChar: function(word) { 13 | return word.slice(-1); 14 | }, 15 | ordinalize: function(dayOfMonth) { 16 | const d = '' + dayOfMonth; 17 | 18 | if (!d) return ''; 19 | if (!parseInt(d, 10)) return ''; 20 | 21 | return str.lastChar(d) === '1' 22 | ? `${dayOfMonth}st` 23 | : str.lastChar(d) === '2' 24 | ? `${dayOfMonth}nd` 25 | : str.lastChar(d) === '3' 26 | ? `${dayOfMonth}rd` 27 | : `${dayOfMonth}th`; 28 | }, 29 | }; 30 | })); 31 | -------------------------------------------------------------------------------- /Chapter 12/publish/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "str", 3 | "version": "1.0.0", 4 | "description": "Simple modular string utils", 5 | "license": "MIT", 6 | "main": "./str.js", 7 | "types": "./str.d.ts" 8 | } 9 | -------------------------------------------------------------------------------- /Chapter 12/publish/str.d.ts: -------------------------------------------------------------------------------- 1 | /* +--------------------Publishing Declarations--------------------+ */ 2 | 3 | /** 4 | * Simple modular string utils 5 | * @license MIT 6 | */ 7 | export declare namespace str { 8 | const version: '1.0.0'; 9 | function lastChar(word: string): string; 10 | function ordinalize(date: number | string): string; 11 | } 12 | -------------------------------------------------------------------------------- /Chapter 12/publish/str.js: -------------------------------------------------------------------------------- 1 | export const str = { 2 | version: '1.0.0', 3 | lastChar: function(word) { 4 | return word.slice(-1); 5 | }, 6 | ordinalize: function(dayOfMonth) { 7 | const d = '' + dayOfMonth; 8 | 9 | if (!d) return ''; 10 | if (!parseInt(d, 10)) return ''; 11 | 12 | return str.lastChar(d) === '1' 13 | ? `${dayOfMonth}st` 14 | : str.lastChar(d) === '2' 15 | ? `${dayOfMonth}nd` 16 | : str.lastChar(d) === '3' 17 | ? `${dayOfMonth}rd` 18 | : `${dayOfMonth}th`; 19 | }, 20 | } 21 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2023 AVA™ 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 | # Ultimate Typescript Handbook 2 | Ultimate Typescript Handbook, published by Orange AVA™ 3 | --------------------------------------------------------------------------------