├── .gitignore ├── README.md ├── images └── typescript-popularity.png ├── javascript ├── 01-declarations.d.ts ├── 01-declarations.js ├── 08-index-signatures.d.ts └── 08-index-signatures.js ├── package-lock.json ├── package.json ├── playground-examples ├── any.js ├── any.ts └── tsconfig.json ├── tsconfig.json ├── typescript-react-example ├── .gitignore ├── README.md ├── package-lock.json ├── package.json ├── public │ ├── favicon.ico │ ├── index.html │ ├── logo192.png │ ├── logo512.png │ ├── manifest.json │ └── robots.txt ├── src │ ├── App.css │ ├── App.test.tsx │ ├── App.tsx │ ├── index.css │ ├── index.tsx │ ├── logo.svg │ ├── react-app-env.d.ts │ ├── reportWebVitals.ts │ └── setupTests.ts └── tsconfig.json ├── workshop-1 ├── 01-declarations.ts ├── 02-interfaces.ts ├── 03-unknown.ts ├── 04-never.ts ├── 05-enums.ts ├── 06-types.ts ├── 07-structural-type-system.ts ├── 08-index-signatures.ts ├── README.md └── compiling │ ├── hello.js │ └── hello.ts ├── workshop-2 ├── 01-everyday-types-01.ts ├── 02-everyday-types-02.ts ├── 03-everyday-types-03.ts ├── 04-narrowing.ts ├── 05-interfaces-implement.ts ├── 06-structural-vs-nominal-types.ts ├── 07-json-types-exercise.ts ├── 08-functions-and-overloading.ts └── README.md └── workshop-3 ├── 01-functions-and-overloading-continued.ts ├── 02-classes-and-access-modifier-keywords.ts ├── 03-top-and-bottom-types.ts ├── 04-type-guards-and-narrowing.ts ├── 05-nullish-values.ts ├── 06-generics.ts ├── 07-dict-map-filter-reduce.ts └── 08-generics-scopes-constraints.ts /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | test 3 | solutions -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # TypeScript Workshop 2 | 3 | 1. [Workshop 1](./workshop-1/README.md) 4 | -------------------------------------------------------------------------------- /images/typescript-popularity.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mjshuff23/typescript-workshop/5a51cca436258c3f892200cdc52872df3e6f3751/images/typescript-popularity.png -------------------------------------------------------------------------------- /javascript/01-declarations.d.ts: -------------------------------------------------------------------------------- 1 | declare const _default: { 2 | myObject: Object; 3 | myAge: number; 4 | }; 5 | export = _default; 6 | -------------------------------------------------------------------------------- /javascript/01-declarations.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | /* 3 | TypeScript knows the JavaScript language and will generate types for you in many cases. 4 | For example in creating a variable and assigning it to a particular value, TypeScript 5 | will use the value as its type. 6 | */ 7 | // Hover over helloWorld, and notice how TypeScript has inferred it's type based on the data we supply the variable with. 8 | let helloWorld = 'Hello, world!'; 9 | // // Can't do, because TypeScript knows this is a variable meant to hold strings 10 | // helloWorld = 12; 11 | // Sometimes we don't have initial values for our variables, so we need to use Type Annotations: 12 | let startTime = new Date(); 13 | // let endTime; // Hover over endTime 14 | let endTime; 15 | // However, you can be very explicit with types if you wish, although it is often unnecessary: 16 | // Syntax: `let/const/var <>: <> [= value]` (Square brackets are optional, meaning we don't have to give it a value immediately unless using const) 17 | let myFirstName; 18 | let myLastName = 'Shuff'; 19 | let myAge = 34; 20 | let isAdult = true; 21 | let myNull = null; 22 | let myUndefined = undefined; 23 | // Arrays must be given an extra parameter specifying the type inside of the array 24 | let myArrayOfNums = [1, 10, 20, 30]; 25 | // The above line can also be done like this: 26 | let mySecondArrayOfNums = [5, 10, 15, 20]; 27 | // String array 28 | let myArrayOfStrings = ['John', 'Doe']; 29 | // Can also be done: 30 | let anotherArrayOfStrings = ['Mike', 'Shuff']; 31 | let myAnyArray = [12, true, null, 'test']; 32 | let mySecondAnyArray = [true, null, 123131]; 33 | let myUser = { 34 | name: 'Mike', 35 | age: 33, 36 | }; 37 | // Arrays 38 | let myUsers = [myUser, myUser]; 39 | let myNums = [1, 6, 20]; 40 | let myWords = ['my', 'words']; 41 | let myChecks = [true, false, false, false, true]; 42 | let myObjs = [ 43 | new Date(), 44 | new Date(), 45 | new Date(), 46 | new Date(), 47 | new Date(), 48 | new Date(), 49 | {}, 50 | ]; 51 | let myNulls = [null, null]; 52 | let myUndefineds = [undefined, undefined, undefined]; 53 | let myArrayOfArrays = [[1, 4, 10], [10, 10, 30], [30]]; 54 | let myAnyArray = ['test', 12, true, 'me', null, 'out']; 55 | let myDateArray = [new Date(), new Date()]; 56 | // Arrays (alternative) 57 | let myAltUsers = [myUser, myUser]; 58 | let myAltNums = [1, 5, 10]; 59 | let myAltWords = ['test', 'me', 'out']; 60 | let myAltChecks = [true, true, false]; 61 | let myAltObjects = [new Date(), new Date()]; 62 | let myAltNulls = [null, null, null]; 63 | let myAltUndefined = [undefined, undefined, undefined]; 64 | let myAltAny = [ 65 | 'test', 66 | 12, 67 | true, 68 | null, 69 | undefined, 70 | 'me', 71 | ['1', '3'], 72 | ]; 73 | let myAltArrayOfArrays = [ 74 | [1, 2, 3], 75 | [4, 5, 6], 76 | ]; 77 | // You can declare a variable of dynamic type in TypeScript with the `any` type 78 | let myDynamicVariable = 'Yo'; 79 | myDynamicVariable = 12; 80 | let myDynamicArray = [ 81 | 'I', 82 | 'should', 83 | 12, 84 | 'probably', 85 | null, 86 | 'avoid', 87 | 'this', 88 | ]; 89 | // Can also be done 90 | let anotherDynamicArray = ['Test', 12, true]; 91 | // Object declaration 92 | let myObject = { 93 | name: 'Yo', 94 | age: 33, 95 | isAdult: true, 96 | }; 97 | // // No can do: 98 | // myObject.name = 12; 99 | console.log(myObject); 100 | // There are a few additions types that TypeScript offers: 101 | // `any` - Allow anything 102 | // `unknown` - Ensure someone using this type declares what the type is 103 | // `never` - It's not possible that this type can happen 104 | // `void` - A function which returns `undefined` or has no return value 105 | let myArray = []; 106 | function populateArray() { 107 | myArray.push(12); 108 | } 109 | console.log(populateArray()); 110 | module.exports = { 111 | myObject, 112 | myAge, 113 | }; 114 | -------------------------------------------------------------------------------- /javascript/08-index-signatures.d.ts: -------------------------------------------------------------------------------- 1 | declare const phones: { 2 | home: { 3 | country: string; 4 | area: string; 5 | number: string; 6 | }; 7 | work: { 8 | country: string; 9 | area: string; 10 | number: string; 11 | }; 12 | mobile: { 13 | country: string; 14 | area: string; 15 | number: string; 16 | }; 17 | fax: { 18 | country: string; 19 | area: string; 20 | number: string; 21 | }; 22 | }; 23 | declare const phonesIndex: { 24 | [key: string]: { 25 | country: string; 26 | area: string; 27 | number: string; 28 | }; 29 | }; 30 | -------------------------------------------------------------------------------- /javascript/08-index-signatures.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | // Index Signatures 3 | /* 4 | Sometimes we need to represent a type for dictionaries, where values of a consistent type are retrievable by keys. 5 | 6 | Consider this collection of phone numbers: 7 | */ 8 | const phones = { 9 | home: { country: '+1', area: '830', number: '210-0910' }, 10 | work: { country: '+1', area: '830', number: '511-0910' }, 11 | mobile: { country: '+1', area: '830', number: '630-0910' }, 12 | fax: { country: '+1', area: '830', number: '32423525' }, 13 | }; 14 | // We can store phone numbers under a `key` (home, work, mobile, etc...) where each number has three properties that are strings 15 | // We can describe this value using an `index signature` 16 | const phonesIndex = {}; 17 | console.log('test'); 18 | console.log(phonesIndex.area.number); 19 | phonesIndex.whatever.number; 20 | -------------------------------------------------------------------------------- /package-lock.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "typescript-workshop", 3 | "version": "1.0.0", 4 | "lockfileVersion": 2, 5 | "requires": true, 6 | "packages": { 7 | "": { 8 | "name": "typescript-workshop", 9 | "version": "1.0.0", 10 | "license": "ISC", 11 | "dependencies": { 12 | "ts-node": "^10.2.1" 13 | }, 14 | "devDependencies": { 15 | "typescript": "^4.4.2" 16 | } 17 | }, 18 | "node_modules/@cspotcode/source-map-consumer": { 19 | "version": "0.8.0", 20 | "resolved": "https://registry.npmjs.org/@cspotcode/source-map-consumer/-/source-map-consumer-0.8.0.tgz", 21 | "integrity": "sha512-41qniHzTU8yAGbCp04ohlmSrZf8bkf/iJsl3V0dRGsQN/5GFfx+LbCSsCpp2gqrqjTVg/K6O8ycoV35JIwAzAg==", 22 | "engines": { 23 | "node": ">= 12" 24 | } 25 | }, 26 | "node_modules/@cspotcode/source-map-support": { 27 | "version": "0.6.1", 28 | "resolved": "https://registry.npmjs.org/@cspotcode/source-map-support/-/source-map-support-0.6.1.tgz", 29 | "integrity": "sha512-DX3Z+T5dt1ockmPdobJS/FAsQPW4V4SrWEhD2iYQT2Cb2tQsiMnYxrcUH9By/Z3B+v0S5LMBkQtV/XOBbpLEOg==", 30 | "dependencies": { 31 | "@cspotcode/source-map-consumer": "0.8.0" 32 | }, 33 | "engines": { 34 | "node": ">=12" 35 | } 36 | }, 37 | "node_modules/@tsconfig/node10": { 38 | "version": "1.0.8", 39 | "resolved": "https://registry.npmjs.org/@tsconfig/node10/-/node10-1.0.8.tgz", 40 | "integrity": "sha512-6XFfSQmMgq0CFLY1MslA/CPUfhIL919M1rMsa5lP2P097N2Wd1sSX0tx1u4olM16fLNhtHZpRhedZJphNJqmZg==" 41 | }, 42 | "node_modules/@tsconfig/node12": { 43 | "version": "1.0.9", 44 | "resolved": "https://registry.npmjs.org/@tsconfig/node12/-/node12-1.0.9.tgz", 45 | "integrity": "sha512-/yBMcem+fbvhSREH+s14YJi18sp7J9jpuhYByADT2rypfajMZZN4WQ6zBGgBKp53NKmqI36wFYDb3yaMPurITw==" 46 | }, 47 | "node_modules/@tsconfig/node14": { 48 | "version": "1.0.1", 49 | "resolved": "https://registry.npmjs.org/@tsconfig/node14/-/node14-1.0.1.tgz", 50 | "integrity": "sha512-509r2+yARFfHHE7T6Puu2jjkoycftovhXRqW328PDXTVGKihlb1P8Z9mMZH04ebyajfRY7dedfGynlrFHJUQCg==" 51 | }, 52 | "node_modules/@tsconfig/node16": { 53 | "version": "1.0.2", 54 | "resolved": "https://registry.npmjs.org/@tsconfig/node16/-/node16-1.0.2.tgz", 55 | "integrity": "sha512-eZxlbI8GZscaGS7kkc/trHTT5xgrjH3/1n2JDwusC9iahPKWMRvRjJSAN5mCXviuTGQ/lHnhvv8Q1YTpnfz9gA==" 56 | }, 57 | "node_modules/@types/node": { 58 | "version": "16.10.2", 59 | "resolved": "https://registry.npmjs.org/@types/node/-/node-16.10.2.tgz", 60 | "integrity": "sha512-zCclL4/rx+W5SQTzFs9wyvvyCwoK9QtBpratqz2IYJ3O8Umrn0m3nsTv0wQBk9sRGpvUe9CwPDrQFB10f1FIjQ==", 61 | "peer": true 62 | }, 63 | "node_modules/acorn": { 64 | "version": "8.5.0", 65 | "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.5.0.tgz", 66 | "integrity": "sha512-yXbYeFy+jUuYd3/CDcg2NkIYE991XYX/bje7LmjJigUciaeO1JR4XxXgCIV1/Zc/dRuFEyw1L0pbA+qynJkW5Q==", 67 | "bin": { 68 | "acorn": "bin/acorn" 69 | }, 70 | "engines": { 71 | "node": ">=0.4.0" 72 | } 73 | }, 74 | "node_modules/acorn-walk": { 75 | "version": "8.2.0", 76 | "resolved": "https://registry.npmjs.org/acorn-walk/-/acorn-walk-8.2.0.tgz", 77 | "integrity": "sha512-k+iyHEuPgSw6SbuDpGQM+06HQUa04DZ3o+F6CSzXMvvI5KMvnaEqXe+YVe555R9nn6GPt404fos4wcgpw12SDA==", 78 | "engines": { 79 | "node": ">=0.4.0" 80 | } 81 | }, 82 | "node_modules/arg": { 83 | "version": "4.1.3", 84 | "resolved": "https://registry.npmjs.org/arg/-/arg-4.1.3.tgz", 85 | "integrity": "sha512-58S9QDqG0Xx27YwPSt9fJxivjYl432YCwfDMfZ+71RAqUrZef7LrKQZ3LHLOwCS4FLNBplP533Zx895SeOCHvA==" 86 | }, 87 | "node_modules/create-require": { 88 | "version": "1.1.1", 89 | "resolved": "https://registry.npmjs.org/create-require/-/create-require-1.1.1.tgz", 90 | "integrity": "sha512-dcKFX3jn0MpIaXjisoRvexIJVEKzaq7z2rZKxf+MSr9TkdmHmsU4m2lcLojrj/FHl8mk5VxMmYA+ftRkP/3oKQ==" 91 | }, 92 | "node_modules/diff": { 93 | "version": "4.0.2", 94 | "resolved": "https://registry.npmjs.org/diff/-/diff-4.0.2.tgz", 95 | "integrity": "sha512-58lmxKSA4BNyLz+HHMUzlOEpg09FV+ev6ZMe3vJihgdxzgcwZ8VoEEPmALCZG9LmqfVoNMMKpttIYTVG6uDY7A==", 96 | "engines": { 97 | "node": ">=0.3.1" 98 | } 99 | }, 100 | "node_modules/make-error": { 101 | "version": "1.3.6", 102 | "resolved": "https://registry.npmjs.org/make-error/-/make-error-1.3.6.tgz", 103 | "integrity": "sha512-s8UhlNe7vPKomQhC1qFelMokr/Sc3AgNbso3n74mVPA5LTZwkB9NlXf4XPamLxJE8h0gh73rM94xvwRT2CVInw==" 104 | }, 105 | "node_modules/ts-node": { 106 | "version": "10.2.1", 107 | "resolved": "https://registry.npmjs.org/ts-node/-/ts-node-10.2.1.tgz", 108 | "integrity": "sha512-hCnyOyuGmD5wHleOQX6NIjJtYVIO8bPP8F2acWkB4W06wdlkgyvJtubO/I9NkI88hCFECbsEgoLc0VNkYmcSfw==", 109 | "dependencies": { 110 | "@cspotcode/source-map-support": "0.6.1", 111 | "@tsconfig/node10": "^1.0.7", 112 | "@tsconfig/node12": "^1.0.7", 113 | "@tsconfig/node14": "^1.0.0", 114 | "@tsconfig/node16": "^1.0.2", 115 | "acorn": "^8.4.1", 116 | "acorn-walk": "^8.1.1", 117 | "arg": "^4.1.0", 118 | "create-require": "^1.1.0", 119 | "diff": "^4.0.1", 120 | "make-error": "^1.1.1", 121 | "yn": "3.1.1" 122 | }, 123 | "bin": { 124 | "ts-node": "dist/bin.js", 125 | "ts-node-cwd": "dist/bin-cwd.js", 126 | "ts-node-script": "dist/bin-script.js", 127 | "ts-node-transpile-only": "dist/bin-transpile.js", 128 | "ts-script": "dist/bin-script-deprecated.js" 129 | }, 130 | "engines": { 131 | "node": ">=12.0.0" 132 | }, 133 | "peerDependencies": { 134 | "@swc/core": ">=1.2.50", 135 | "@swc/wasm": ">=1.2.50", 136 | "@types/node": "*", 137 | "typescript": ">=2.7" 138 | }, 139 | "peerDependenciesMeta": { 140 | "@swc/core": { 141 | "optional": true 142 | }, 143 | "@swc/wasm": { 144 | "optional": true 145 | } 146 | } 147 | }, 148 | "node_modules/typescript": { 149 | "version": "4.4.2", 150 | "resolved": "https://registry.npmjs.org/typescript/-/typescript-4.4.2.tgz", 151 | "integrity": "sha512-gzP+t5W4hdy4c+68bfcv0t400HVJMMd2+H9B7gae1nQlBzCqvrXX+6GL/b3GAgyTH966pzrZ70/fRjwAtZksSQ==", 152 | "bin": { 153 | "tsc": "bin/tsc", 154 | "tsserver": "bin/tsserver" 155 | }, 156 | "engines": { 157 | "node": ">=4.2.0" 158 | } 159 | }, 160 | "node_modules/yn": { 161 | "version": "3.1.1", 162 | "resolved": "https://registry.npmjs.org/yn/-/yn-3.1.1.tgz", 163 | "integrity": "sha512-Ux4ygGWsu2c7isFWe8Yu1YluJmqVhxqK2cLXNQA5AcC3QfbGNpM7fu0Y8b/z16pXLnFxZYvWhd3fhBY9DLmC6Q==", 164 | "engines": { 165 | "node": ">=6" 166 | } 167 | } 168 | }, 169 | "dependencies": { 170 | "@cspotcode/source-map-consumer": { 171 | "version": "0.8.0", 172 | "resolved": "https://registry.npmjs.org/@cspotcode/source-map-consumer/-/source-map-consumer-0.8.0.tgz", 173 | "integrity": "sha512-41qniHzTU8yAGbCp04ohlmSrZf8bkf/iJsl3V0dRGsQN/5GFfx+LbCSsCpp2gqrqjTVg/K6O8ycoV35JIwAzAg==" 174 | }, 175 | "@cspotcode/source-map-support": { 176 | "version": "0.6.1", 177 | "resolved": "https://registry.npmjs.org/@cspotcode/source-map-support/-/source-map-support-0.6.1.tgz", 178 | "integrity": "sha512-DX3Z+T5dt1ockmPdobJS/FAsQPW4V4SrWEhD2iYQT2Cb2tQsiMnYxrcUH9By/Z3B+v0S5LMBkQtV/XOBbpLEOg==", 179 | "requires": { 180 | "@cspotcode/source-map-consumer": "0.8.0" 181 | } 182 | }, 183 | "@tsconfig/node10": { 184 | "version": "1.0.8", 185 | "resolved": "https://registry.npmjs.org/@tsconfig/node10/-/node10-1.0.8.tgz", 186 | "integrity": "sha512-6XFfSQmMgq0CFLY1MslA/CPUfhIL919M1rMsa5lP2P097N2Wd1sSX0tx1u4olM16fLNhtHZpRhedZJphNJqmZg==" 187 | }, 188 | "@tsconfig/node12": { 189 | "version": "1.0.9", 190 | "resolved": "https://registry.npmjs.org/@tsconfig/node12/-/node12-1.0.9.tgz", 191 | "integrity": "sha512-/yBMcem+fbvhSREH+s14YJi18sp7J9jpuhYByADT2rypfajMZZN4WQ6zBGgBKp53NKmqI36wFYDb3yaMPurITw==" 192 | }, 193 | "@tsconfig/node14": { 194 | "version": "1.0.1", 195 | "resolved": "https://registry.npmjs.org/@tsconfig/node14/-/node14-1.0.1.tgz", 196 | "integrity": "sha512-509r2+yARFfHHE7T6Puu2jjkoycftovhXRqW328PDXTVGKihlb1P8Z9mMZH04ebyajfRY7dedfGynlrFHJUQCg==" 197 | }, 198 | "@tsconfig/node16": { 199 | "version": "1.0.2", 200 | "resolved": "https://registry.npmjs.org/@tsconfig/node16/-/node16-1.0.2.tgz", 201 | "integrity": "sha512-eZxlbI8GZscaGS7kkc/trHTT5xgrjH3/1n2JDwusC9iahPKWMRvRjJSAN5mCXviuTGQ/lHnhvv8Q1YTpnfz9gA==" 202 | }, 203 | "@types/node": { 204 | "version": "16.10.2", 205 | "resolved": "https://registry.npmjs.org/@types/node/-/node-16.10.2.tgz", 206 | "integrity": "sha512-zCclL4/rx+W5SQTzFs9wyvvyCwoK9QtBpratqz2IYJ3O8Umrn0m3nsTv0wQBk9sRGpvUe9CwPDrQFB10f1FIjQ==", 207 | "peer": true 208 | }, 209 | "acorn": { 210 | "version": "8.5.0", 211 | "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.5.0.tgz", 212 | "integrity": "sha512-yXbYeFy+jUuYd3/CDcg2NkIYE991XYX/bje7LmjJigUciaeO1JR4XxXgCIV1/Zc/dRuFEyw1L0pbA+qynJkW5Q==" 213 | }, 214 | "acorn-walk": { 215 | "version": "8.2.0", 216 | "resolved": "https://registry.npmjs.org/acorn-walk/-/acorn-walk-8.2.0.tgz", 217 | "integrity": "sha512-k+iyHEuPgSw6SbuDpGQM+06HQUa04DZ3o+F6CSzXMvvI5KMvnaEqXe+YVe555R9nn6GPt404fos4wcgpw12SDA==" 218 | }, 219 | "arg": { 220 | "version": "4.1.3", 221 | "resolved": "https://registry.npmjs.org/arg/-/arg-4.1.3.tgz", 222 | "integrity": "sha512-58S9QDqG0Xx27YwPSt9fJxivjYl432YCwfDMfZ+71RAqUrZef7LrKQZ3LHLOwCS4FLNBplP533Zx895SeOCHvA==" 223 | }, 224 | "create-require": { 225 | "version": "1.1.1", 226 | "resolved": "https://registry.npmjs.org/create-require/-/create-require-1.1.1.tgz", 227 | "integrity": "sha512-dcKFX3jn0MpIaXjisoRvexIJVEKzaq7z2rZKxf+MSr9TkdmHmsU4m2lcLojrj/FHl8mk5VxMmYA+ftRkP/3oKQ==" 228 | }, 229 | "diff": { 230 | "version": "4.0.2", 231 | "resolved": "https://registry.npmjs.org/diff/-/diff-4.0.2.tgz", 232 | "integrity": "sha512-58lmxKSA4BNyLz+HHMUzlOEpg09FV+ev6ZMe3vJihgdxzgcwZ8VoEEPmALCZG9LmqfVoNMMKpttIYTVG6uDY7A==" 233 | }, 234 | "make-error": { 235 | "version": "1.3.6", 236 | "resolved": "https://registry.npmjs.org/make-error/-/make-error-1.3.6.tgz", 237 | "integrity": "sha512-s8UhlNe7vPKomQhC1qFelMokr/Sc3AgNbso3n74mVPA5LTZwkB9NlXf4XPamLxJE8h0gh73rM94xvwRT2CVInw==" 238 | }, 239 | "ts-node": { 240 | "version": "10.2.1", 241 | "resolved": "https://registry.npmjs.org/ts-node/-/ts-node-10.2.1.tgz", 242 | "integrity": "sha512-hCnyOyuGmD5wHleOQX6NIjJtYVIO8bPP8F2acWkB4W06wdlkgyvJtubO/I9NkI88hCFECbsEgoLc0VNkYmcSfw==", 243 | "requires": { 244 | "@cspotcode/source-map-support": "0.6.1", 245 | "@tsconfig/node10": "^1.0.7", 246 | "@tsconfig/node12": "^1.0.7", 247 | "@tsconfig/node14": "^1.0.0", 248 | "@tsconfig/node16": "^1.0.2", 249 | "acorn": "^8.4.1", 250 | "acorn-walk": "^8.1.1", 251 | "arg": "^4.1.0", 252 | "create-require": "^1.1.0", 253 | "diff": "^4.0.1", 254 | "make-error": "^1.1.1", 255 | "yn": "3.1.1" 256 | } 257 | }, 258 | "typescript": { 259 | "version": "4.4.2", 260 | "resolved": "https://registry.npmjs.org/typescript/-/typescript-4.4.2.tgz", 261 | "integrity": "sha512-gzP+t5W4hdy4c+68bfcv0t400HVJMMd2+H9B7gae1nQlBzCqvrXX+6GL/b3GAgyTH966pzrZ70/fRjwAtZksSQ==" 262 | }, 263 | "yn": { 264 | "version": "3.1.1", 265 | "resolved": "https://registry.npmjs.org/yn/-/yn-3.1.1.tgz", 266 | "integrity": "sha512-Ux4ygGWsu2c7isFWe8Yu1YluJmqVhxqK2cLXNQA5AcC3QfbGNpM7fu0Y8b/z16pXLnFxZYvWhd3fhBY9DLmC6Q==" 267 | } 268 | } 269 | } 270 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "typescript-workshop", 3 | "version": "1.0.0", 4 | "description": "1. [Workshop 1](./workshop-1/README.md)", 5 | "main": "index.js", 6 | "scripts": { 7 | "test": "echo \"Error: no test specified\" && exit 1", 8 | "dev": "tsc --watch --preserveWatchOutput --jsx react-jsx" 9 | }, 10 | "repository": { 11 | "type": "git", 12 | "url": "git+https://github.com/mjshuff23/typescript-workshop.git" 13 | }, 14 | "keywords": [], 15 | "author": "", 16 | "license": "ISC", 17 | "bugs": { 18 | "url": "https://github.com/mjshuff23/typescript-workshop/issues" 19 | }, 20 | "homepage": "https://github.com/mjshuff23/typescript-workshop#readme", 21 | "devDependencies": { 22 | "typescript": "^4.4.2" 23 | }, 24 | "dependencies": { 25 | "ts-node": "^10.2.1" 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /playground-examples/any.js: -------------------------------------------------------------------------------- 1 | // Any is the TypeScript escape clause. You can use any to 2 | // either declare a section of your code to be dynamic and 3 | // JavaScript like, or to work around limitations in the 4 | // type system. 5 | // A good case for any is JSON parsing: 6 | const myObject = JSON.parse("{}"); 7 | debug("a string"); 8 | debug(23); 9 | debug({ color: "blue" }); 10 | swap(pair); 11 | // The call to swap is allowed because the argument can be 12 | // matched by replacing the first any in pair with number 13 | // and the second `any` with string. 14 | // If tuples are new to you, see: example:tuples 15 | // Unknown is a sibling type to any, if any is about saying 16 | // "I know what's best", then unknown is a way to say "I'm 17 | // not sure what is best, so you need to tell TS the type" 18 | // example:unknown-and-never 19 | -------------------------------------------------------------------------------- /playground-examples/any.ts: -------------------------------------------------------------------------------- 1 | // Any is the TypeScript escape clause. You can use any to 2 | // either declare a section of your code to be dynamic and 3 | // JavaScript like, or to work around limitations in the 4 | // type system. 5 | 6 | // A good case for any is JSON parsing: 7 | const myObject = JSON.parse("{}"); 8 | 9 | // // Any declares to TypeScript to trust your code as being 10 | // // safe because you know more about it. Even if that is 11 | // // not strictly true. For example, this code would crash: 12 | // myObject.x.y.z; 13 | 14 | // Using an any gives you the ability to write code closer to 15 | // original JavaScript with the trade-off of type safety. 16 | 17 | // any is much like a 'type wildcard' which you can replace 18 | // with any type (except never) to make one type assignable 19 | // to the other. 20 | 21 | declare function debug(value: any): void; 22 | 23 | debug("a string"); 24 | debug(23); 25 | debug({ color: "blue" }); 26 | 27 | // Each call to debug is allowed because you could replace the 28 | // any with the type of the argument to match. 29 | 30 | // TypeScript will take into account the position of the 31 | // anys in different forms, for example with these tuples 32 | // for the function argument. 33 | 34 | declare function swap(x: [ number, string ]): [ string, number ]; 35 | declare const pair: [ any, any ]; 36 | swap(pair); 37 | 38 | // The call to swap is allowed because the argument can be 39 | // matched by replacing the first any in pair with number 40 | // and the second `any` with string. 41 | 42 | // If tuples are new to you, see: example:tuples 43 | 44 | // Unknown is a sibling type to any, if any is about saying 45 | // "I know what's best", then unknown is a way to say "I'm 46 | // not sure what is best, so you need to tell TS the type" 47 | // example:unknown-and-never 48 | -------------------------------------------------------------------------------- /playground-examples/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | /* Basic Options */ 4 | // "incremental": true, /* Enable incremental compilation */ 5 | "target": "es5", /* Specify ECMAScript target version: 'ES3' (default), 'ES5', 'ES2015', 'ES2016', 'ES2017', 'ES2018', 'ES2019', 'ES2020', or 'ESNEXT'. */ 6 | "module": "commonjs", /* Specify module code generation: 'none', 'commonjs', 'amd', 'system', 'umd', 'es2015', 'es2020', or 'ESNext'. */ 7 | // "lib": [], /* Specify library files to be included in the compilation. */ 8 | // "allowJs": true, /* Allow javascript files to be compiled. */ 9 | // "checkJs": true, /* Report errors in .js files. */ 10 | // "jsx": "preserve", /* Specify JSX code generation: 'preserve', 'react-native', or 'react'. */ 11 | // "declaration": true, /* Generates corresponding '.d.ts' file. */ 12 | // "declarationMap": true, /* Generates a sourcemap for each corresponding '.d.ts' file. */ 13 | // "sourceMap": true, /* Generates corresponding '.map' file. */ 14 | // "outFile": "./", /* Concatenate and emit output to single file. */ 15 | // "outDir": "./", /* Redirect output structure to the directory. */ 16 | // "rootDir": "./", /* Specify the root directory of input files. Use to control the output directory structure with --outDir. */ 17 | // "composite": true, /* Enable project compilation */ 18 | // "tsBuildInfoFile": "./", /* Specify file to store incremental compilation information */ 19 | // "removeComments": true, /* Do not emit comments to output. */ 20 | // "noEmit": true, /* Do not emit outputs. */ 21 | // "importHelpers": true, /* Import emit helpers from 'tslib'. */ 22 | // "downlevelIteration": true, /* Provide full support for iterables in 'for-of', spread, and destructuring when targeting 'ES5' or 'ES3'. */ 23 | // "isolatedModules": true, /* Transpile each file as a separate module (similar to 'ts.transpileModule'). */ 24 | 25 | /* Strict Type-Checking Options */ 26 | "strict": true, /* Enable all strict type-checking options. */ 27 | // "noImplicitAny": true, /* Raise error on expressions and declarations with an implied 'any' type. */ 28 | // "strictNullChecks": true, /* Enable strict null checks. */ 29 | // "strictFunctionTypes": true, /* Enable strict checking of function types. */ 30 | // "strictBindCallApply": true, /* Enable strict 'bind', 'call', and 'apply' methods on functions. */ 31 | // "strictPropertyInitialization": true, /* Enable strict checking of property initialization in classes. */ 32 | // "noImplicitThis": true, /* Raise error on 'this' expressions with an implied 'any' type. */ 33 | // "alwaysStrict": true, /* Parse in strict mode and emit "use strict" for each source file. */ 34 | 35 | /* Additional Checks */ 36 | // "noUnusedLocals": true, /* Report errors on unused locals. */ 37 | // "noUnusedParameters": true, /* Report errors on unused parameters. */ 38 | // "noImplicitReturns": true, /* Report error when not all code paths in function return a value. */ 39 | // "noFallthroughCasesInSwitch": true, /* Report errors for fallthrough cases in switch statement. */ 40 | 41 | /* Module Resolution Options */ 42 | // "moduleResolution": "node", /* Specify module resolution strategy: 'node' (Node.js) or 'classic' (TypeScript pre-1.6). */ 43 | // "baseUrl": "./", /* Base directory to resolve non-absolute module names. */ 44 | // "paths": {}, /* A series of entries which re-map imports to lookup locations relative to the 'baseUrl'. */ 45 | // "rootDirs": [], /* List of root folders whose combined content represents the structure of the project at runtime. */ 46 | // "typeRoots": [], /* List of folders to include type definitions from. */ 47 | // "types": [], /* Type declaration files to be included in compilation. */ 48 | // "allowSyntheticDefaultImports": true, /* Allow default imports from modules with no default export. This does not affect code emit, just typechecking. */ 49 | "esModuleInterop": true, /* Enables emit interoperability between CommonJS and ES Modules via creation of namespace objects for all imports. Implies 'allowSyntheticDefaultImports'. */ 50 | // "preserveSymlinks": true, /* Do not resolve the real path of symlinks. */ 51 | // "allowUmdGlobalAccess": true, /* Allow accessing UMD globals from modules. */ 52 | 53 | /* Source Map Options */ 54 | // "sourceRoot": "", /* Specify the location where debugger should locate TypeScript files instead of source locations. */ 55 | // "mapRoot": "", /* Specify the location where debugger should locate map files instead of generated locations. */ 56 | // "inlineSourceMap": true, /* Emit a single file with source maps instead of having a separate file. */ 57 | // "inlineSources": true, /* Emit the source alongside the sourcemaps within a single file; requires '--inlineSourceMap' or '--sourceMap' to be set. */ 58 | 59 | /* Experimental Options */ 60 | // "experimentalDecorators": true, /* Enables experimental support for ES7 decorators. */ 61 | // "emitDecoratorMetadata": true, /* Enables experimental support for emitting type metadata for decorators. */ 62 | 63 | /* Advanced Options */ 64 | "forceConsistentCasingInFileNames": true /* Disallow inconsistently-cased references to the same file. */ 65 | } 66 | } 67 | -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | /* Visit https://aka.ms/tsconfig.json to read more about this file */ 4 | /* Basic Options */ 5 | // "incremental": true, /* Enable incremental compilation */ 6 | "target": "ESNEXT", /* Specify ECMAScript target version: 'ES3' (default), 'ES5', 'ES2015', 'ES2016', 'ES2017', 'ES2018', 'ES2019', 'ES2020', 'ES2021', or 'ESNEXT'. */ 7 | "module": "commonjs", /* Specify module code generation: 'none', 'commonjs', 'amd', 'system', 'umd', 'es2015', 'es2020', or 'ESNext'. */ 8 | "lib": [ 9 | "ES2019", 10 | "DOM", 11 | ], /* Specify library files to be included in the compilation. */ 12 | // "allowJs": true, /* Allow javascript files to be compiled. */ 13 | // "checkJs": true, /* Report errors in .js files. */ 14 | // "jsx": "preserve", /* Specify JSX code generation: 'preserve', 'react-native', 'react', 'react-jsx' or 'react-jsxdev'. */ 15 | "declaration": true, /* Generates corresponding '.d.ts' file. */ 16 | // "declarationMap": true, /* Generates a sourcemap for each corresponding '.d.ts' file. */ 17 | // "sourceMap": true, /* Generates corresponding '.map' file. */ 18 | // "outFile": "./", /* Concatenate and emit output to single file. */ 19 | "outDir": "./javascript", /* Redirect output structure to the directory. */ 20 | // "rootDir": "./", /* Specify the root directory of input files. Use to control the output directory structure with --outDir. */ 21 | // "composite": true, /* Enable project compilation */ 22 | // "tsBuildInfoFile": "./", /* Specify file to store incremental compilation information */ 23 | // "removeComments": true, /* Do not emit comments to output. */ 24 | // "noEmit": true, /* Do not emit outputs. */ 25 | // "importHelpers": true, /* Import emit helpers from 'tslib'. */ 26 | // "downlevelIteration": true, /* Provide full support for iterables in 'for-of', spread, and destructuring when targeting 'ES5' or 'ES3'. */ 27 | // "isolatedModules": true, /* Transpile each file as a separate module (similar to 'ts.transpileModule'). */ 28 | /* Strict Type-Checking Options */ 29 | "strict": true, /* Enable all strict type-checking options. */ 30 | // "noImplicitAny": true, /* Raise error on expressions and declarations with an implied 'any' type. */ 31 | // "strictNullChecks": true, /* Enable strict null checks. */ 32 | // "strictFunctionTypes": true, /* Enable strict checking of function types. */ 33 | // "strictBindCallApply": true, /* Enable strict 'bind', 'call', and 'apply' methods on functions. */ 34 | // "strictPropertyInitialization": true, /* Enable strict checking of property initialization in classes. */ 35 | // "noImplicitThis": true, /* Raise error on 'this' expressions with an implied 'any' type. */ 36 | // "alwaysStrict": true, /* Parse in strict mode and emit "use strict" for each source file. */ 37 | /* Additional Checks */ 38 | // "noUnusedLocals": true, /* Report errors on unused locals. */ 39 | // "noUnusedParameters": true, /* Report errors on unused parameters. */ 40 | // "noImplicitReturns": true, /* Report error when not all code paths in function return a value. */ 41 | // "noFallthroughCasesInSwitch": true, /* Report errors for fallthrough cases in switch statement. */ 42 | // "noUncheckedIndexedAccess": true, /* Include 'undefined' in index signature results */ 43 | // "noImplicitOverride": true, /* Ensure overriding members in derived classes are marked with an 'override' modifier. */ 44 | // "noPropertyAccessFromIndexSignature": true, /* Require undeclared properties from index signatures to use element accesses. */ 45 | /* Module Resolution Options */ 46 | // "moduleResolution": "node", /* Specify module resolution strategy: 'node' (Node.js) or 'classic' (TypeScript pre-1.6). */ 47 | // "baseUrl": "./", /* Base directory to resolve non-absolute module names. */ 48 | // "paths": {}, /* A series of entries which re-map imports to lookup locations relative to the 'baseUrl'. */ 49 | // "rootDirs": [], /* List of root folders whose combined content represents the structure of the project at runtime. */ 50 | // "typeRoots": [], /* List of folders to include type definitions from. */ 51 | // "types": [], /* Type declaration files to be included in compilation. */ 52 | // "allowSyntheticDefaultImports": true, /* Allow default imports from modules with no default export. This does not affect code emit, just typechecking. */ 53 | "esModuleInterop": true, /* Enables emit interoperability between CommonJS and ES Modules via creation of namespace objects for all imports. Implies 'allowSyntheticDefaultImports'. */ 54 | // "preserveSymlinks": true, /* Do not resolve the real path of symlinks. */ 55 | // "allowUmdGlobalAccess": true, /* Allow accessing UMD globals from modules. */ 56 | /* Source Map Options */ 57 | // "sourceRoot": "", /* Specify the location where debugger should locate TypeScript files instead of source locations. */ 58 | // "mapRoot": "", /* Specify the location where debugger should locate map files instead of generated locations. */ 59 | // "inlineSourceMap": true, /* Emit a single file with source maps instead of having a separate file. */ 60 | // "inlineSources": true, /* Emit the source alongside the sourcemaps within a single file; requires '--inlineSourceMap' or '--sourceMap' to be set. */ 61 | /* Experimental Options */ 62 | // "experimentalDecorators": true, /* Enables experimental support for ES7 decorators. */ 63 | // "emitDecoratorMetadata": true, /* Enables experimental support for emitting type metadata for decorators. */ 64 | /* Advanced Options */ 65 | "skipLibCheck": true, /* Skip type checking of declaration files. */ 66 | "forceConsistentCasingInFileNames": true /* Disallow inconsistently-cased references to the same file. */ 67 | }, 68 | "include": [ 69 | ], 70 | "files": [ 71 | // "./workshop-1/01-declarations.ts", 72 | // "./workshop-1/08-index-signatures.ts" 73 | "./workshop-2/01-everyday-types-01.ts", 74 | "./workshop-2/02-everyday-types-02.ts", 75 | "./workshop-2/03-everyday-types-03.ts", 76 | "./workshop-2/04-narrowing.ts", 77 | "./workshop-2/05-interfaces-implement.ts", 78 | "./workshop-2/06-structural-vs-nominal-types.ts", 79 | "./workshop-2/07-json-types-exercise.ts", 80 | "./workshop-2/08-functions-and-overloading.ts", 81 | // "./workshop-3/01-functions-and-overloading-continued.ts", 82 | // "./workshop-3/02-classes-and-access-modifier-keywords.ts", 83 | // "./workshop-3/03-top-and-bottom-types.ts", 84 | // "./workshop-3/04-type-guards-and-narrowing.ts", 85 | // "./workshop-3/05-nullish-values.ts", 86 | // "./workshop-3/06-generics.ts", 87 | // "./workshop-3/07-dict-map-filter-reduce.ts", 88 | // "./workshop-3/08-generics-scopes-constraints.ts" 89 | ], 90 | "exclude": [ 91 | // "./javascript", 92 | // "./workshop-1/*", 93 | // "./workshop-2/*" 94 | ] 95 | } 96 | -------------------------------------------------------------------------------- /typescript-react-example/.gitignore: -------------------------------------------------------------------------------- 1 | # See https://help.github.com/articles/ignoring-files/ for more about ignoring files. 2 | 3 | # dependencies 4 | /node_modules 5 | /.pnp 6 | .pnp.js 7 | 8 | # testing 9 | /coverage 10 | 11 | # production 12 | /build 13 | 14 | # misc 15 | .DS_Store 16 | .env.local 17 | .env.development.local 18 | .env.test.local 19 | .env.production.local 20 | 21 | npm-debug.log* 22 | yarn-debug.log* 23 | yarn-error.log* 24 | -------------------------------------------------------------------------------- /typescript-react-example/README.md: -------------------------------------------------------------------------------- 1 | # Getting Started with Create React App 2 | 3 | This project was bootstrapped with [Create React App](https://github.com/facebook/create-react-app). 4 | 5 | ## Available Scripts 6 | 7 | In the project directory, you can run: 8 | 9 | ### `npm start` 10 | 11 | Runs the app in the development mode.\ 12 | Open [http://localhost:3000](http://localhost:3000) to view it in the browser. 13 | 14 | The page will reload if you make edits.\ 15 | You will also see any lint errors in the console. 16 | 17 | ### `npm test` 18 | 19 | Launches the test runner in the interactive watch mode.\ 20 | See the section about [running tests](https://facebook.github.io/create-react-app/docs/running-tests) for more information. 21 | 22 | ### `npm run build` 23 | 24 | Builds the app for production to the `build` folder.\ 25 | It correctly bundles React in production mode and optimizes the build for the best performance. 26 | 27 | The build is minified and the filenames include the hashes.\ 28 | Your app is ready to be deployed! 29 | 30 | See the section about [deployment](https://facebook.github.io/create-react-app/docs/deployment) for more information. 31 | 32 | ### `npm run eject` 33 | 34 | **Note: this is a one-way operation. Once you `eject`, you can’t go back!** 35 | 36 | If you aren’t satisfied with the build tool and configuration choices, you can `eject` at any time. This command will remove the single build dependency from your project. 37 | 38 | Instead, it will copy all the configuration files and the transitive dependencies (webpack, Babel, ESLint, etc) right into your project so you have full control over them. All of the commands except `eject` will still work, but they will point to the copied scripts so you can tweak them. At this point you’re on your own. 39 | 40 | You don’t have to ever use `eject`. The curated feature set is suitable for small and middle deployments, and you shouldn’t feel obligated to use this feature. However we understand that this tool wouldn’t be useful if you couldn’t customize it when you are ready for it. 41 | 42 | ## Learn More 43 | 44 | You can learn more in the [Create React App documentation](https://facebook.github.io/create-react-app/docs/getting-started). 45 | 46 | To learn React, check out the [React documentation](https://reactjs.org/). 47 | -------------------------------------------------------------------------------- /typescript-react-example/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "typescript-react-example", 3 | "version": "0.1.0", 4 | "private": true, 5 | "dependencies": { 6 | "@testing-library/jest-dom": "^5.14.1", 7 | "@testing-library/react": "^11.2.7", 8 | "@testing-library/user-event": "^12.8.3", 9 | "@types/node": "^12.20.24", 10 | "@types/react": "^17.0.20", 11 | "@types/react-dom": "^17.0.9", 12 | "react": "^17.0.2", 13 | "react-dom": "^17.0.2", 14 | "react-scripts": "4.0.3", 15 | "typescript": "^4.4.3", 16 | "web-vitals": "^1.1.2" 17 | }, 18 | "scripts": { 19 | "start": "react-scripts start", 20 | "build": "react-scripts build", 21 | "test": "react-scripts test", 22 | "eject": "react-scripts eject" 23 | }, 24 | "eslintConfig": { 25 | "extends": [ 26 | "react-app", 27 | "react-app/jest" 28 | ] 29 | }, 30 | "browserslist": { 31 | "production": [ 32 | ">0.2%", 33 | "not dead", 34 | "not op_mini all" 35 | ], 36 | "development": [ 37 | "last 1 chrome version", 38 | "last 1 firefox version", 39 | "last 1 safari version" 40 | ] 41 | }, 42 | "devDependencies": { 43 | "@types/jest": "^27.0.1" 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /typescript-react-example/public/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mjshuff23/typescript-workshop/5a51cca436258c3f892200cdc52872df3e6f3751/typescript-react-example/public/favicon.ico -------------------------------------------------------------------------------- /typescript-react-example/public/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 12 | 13 | 17 | 18 | 27 | React App 28 | 29 | 30 | 31 |
32 | 42 | 43 | 44 | -------------------------------------------------------------------------------- /typescript-react-example/public/logo192.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mjshuff23/typescript-workshop/5a51cca436258c3f892200cdc52872df3e6f3751/typescript-react-example/public/logo192.png -------------------------------------------------------------------------------- /typescript-react-example/public/logo512.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mjshuff23/typescript-workshop/5a51cca436258c3f892200cdc52872df3e6f3751/typescript-react-example/public/logo512.png -------------------------------------------------------------------------------- /typescript-react-example/public/manifest.json: -------------------------------------------------------------------------------- 1 | { 2 | "short_name": "React App", 3 | "name": "Create React App Sample", 4 | "icons": [ 5 | { 6 | "src": "favicon.ico", 7 | "sizes": "64x64 32x32 24x24 16x16", 8 | "type": "image/x-icon" 9 | }, 10 | { 11 | "src": "logo192.png", 12 | "type": "image/png", 13 | "sizes": "192x192" 14 | }, 15 | { 16 | "src": "logo512.png", 17 | "type": "image/png", 18 | "sizes": "512x512" 19 | } 20 | ], 21 | "start_url": ".", 22 | "display": "standalone", 23 | "theme_color": "#000000", 24 | "background_color": "#ffffff" 25 | } 26 | -------------------------------------------------------------------------------- /typescript-react-example/public/robots.txt: -------------------------------------------------------------------------------- 1 | # https://www.robotstxt.org/robotstxt.html 2 | User-agent: * 3 | Disallow: 4 | -------------------------------------------------------------------------------- /typescript-react-example/src/App.css: -------------------------------------------------------------------------------- 1 | .App { 2 | text-align: center; 3 | } 4 | 5 | .App-logo { 6 | height: 40vmin; 7 | pointer-events: none; 8 | } 9 | 10 | @media (prefers-reduced-motion: no-preference) { 11 | .App-logo { 12 | animation: App-logo-spin infinite 20s linear; 13 | } 14 | } 15 | 16 | .App-header { 17 | background-color: #282c34; 18 | min-height: 100vh; 19 | display: flex; 20 | flex-direction: column; 21 | align-items: center; 22 | justify-content: center; 23 | font-size: calc(10px + 2vmin); 24 | color: white; 25 | } 26 | 27 | .App-link { 28 | color: #61dafb; 29 | } 30 | 31 | @keyframes App-logo-spin { 32 | from { 33 | transform: rotate(0deg); 34 | } 35 | to { 36 | transform: rotate(360deg); 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /typescript-react-example/src/App.test.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import { render, screen } from '@testing-library/react'; 3 | import App from './App'; 4 | 5 | test('renders learn react link', () => { 6 | render(); 7 | const linkElement = screen.getByText(/learn react/i); 8 | expect(linkElement).toBeInTheDocument(); 9 | }); 10 | -------------------------------------------------------------------------------- /typescript-react-example/src/App.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import logo from './logo.svg'; 3 | import './App.css'; 4 | 5 | function App() { 6 | return ( 7 |
8 |
9 | logo 10 |

11 | Edit src/App.tsx and save to reload. 12 |

13 | 19 | Learn React 20 | 21 |
22 |
23 | ); 24 | } 25 | 26 | export default App; 27 | -------------------------------------------------------------------------------- /typescript-react-example/src/index.css: -------------------------------------------------------------------------------- 1 | body { 2 | margin: 0; 3 | font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', 'Roboto', 'Oxygen', 4 | 'Ubuntu', 'Cantarell', 'Fira Sans', 'Droid Sans', 'Helvetica Neue', 5 | sans-serif; 6 | -webkit-font-smoothing: antialiased; 7 | -moz-osx-font-smoothing: grayscale; 8 | } 9 | 10 | code { 11 | font-family: source-code-pro, Menlo, Monaco, Consolas, 'Courier New', 12 | monospace; 13 | } 14 | -------------------------------------------------------------------------------- /typescript-react-example/src/index.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import ReactDOM from 'react-dom'; 3 | import './index.css'; 4 | import App from './App'; 5 | import reportWebVitals from './reportWebVitals'; 6 | 7 | ReactDOM.render( 8 | 9 | 10 | , 11 | document.getElementById('root') 12 | ); 13 | 14 | // If you want to start measuring performance in your app, pass a function 15 | // to log results (for example: reportWebVitals(console.log)) 16 | // or send to an analytics endpoint. Learn more: https://bit.ly/CRA-vitals 17 | reportWebVitals(); 18 | -------------------------------------------------------------------------------- /typescript-react-example/src/logo.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /typescript-react-example/src/react-app-env.d.ts: -------------------------------------------------------------------------------- 1 | /// 2 | -------------------------------------------------------------------------------- /typescript-react-example/src/reportWebVitals.ts: -------------------------------------------------------------------------------- 1 | import { ReportHandler } from 'web-vitals'; 2 | 3 | const reportWebVitals = (onPerfEntry?: ReportHandler) => { 4 | if (onPerfEntry && onPerfEntry instanceof Function) { 5 | import('web-vitals').then(({ getCLS, getFID, getFCP, getLCP, getTTFB }) => { 6 | getCLS(onPerfEntry); 7 | getFID(onPerfEntry); 8 | getFCP(onPerfEntry); 9 | getLCP(onPerfEntry); 10 | getTTFB(onPerfEntry); 11 | }); 12 | } 13 | }; 14 | 15 | export default reportWebVitals; 16 | -------------------------------------------------------------------------------- /typescript-react-example/src/setupTests.ts: -------------------------------------------------------------------------------- 1 | // jest-dom adds custom jest matchers for asserting on DOM nodes. 2 | // allows you to do things like: 3 | // expect(element).toHaveTextContent(/react/i) 4 | // learn more: https://github.com/testing-library/jest-dom 5 | import '@testing-library/jest-dom'; 6 | -------------------------------------------------------------------------------- /typescript-react-example/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "target": "es5", 4 | "lib": [ 5 | "dom", 6 | "dom.iterable", 7 | "esnext" 8 | ], 9 | "allowJs": true, 10 | "skipLibCheck": true, 11 | "esModuleInterop": true, 12 | "allowSyntheticDefaultImports": true, 13 | "strict": true, 14 | "forceConsistentCasingInFileNames": true, 15 | "noFallthroughCasesInSwitch": true, 16 | "module": "esnext", 17 | "moduleResolution": "node", 18 | "resolveJsonModule": true, 19 | "isolatedModules": true, 20 | "noEmit": true, 21 | "jsx": "react-jsx" 22 | }, 23 | "include": [ 24 | "src" 25 | ] 26 | } 27 | -------------------------------------------------------------------------------- /workshop-1/01-declarations.ts: -------------------------------------------------------------------------------- 1 | /* 2 | TypeScript knows the JavaScript language and will generate types for you in many cases. 3 | For example in creating a variable and assigning it to a particular value, TypeScript 4 | will use the value as its type. 5 | */ 6 | 7 | // Hover over helloWorld, and notice how TypeScript has inferred it's type based on the data we supply the variable with. 8 | let helloWorld = 'Hello, world!'; 9 | // // Can't do, because TypeScript knows this is a variable meant to hold strings 10 | // helloWorld = 12; 11 | 12 | // Sometimes we don't have initial values for our variables, so we need to use Type Annotations: 13 | let startTime = new Date(); 14 | // let endTime; // Hover over endTime 15 | let endTime: Date; 16 | 17 | // However, you can be very explicit with types if you wish, although it is often unnecessary: 18 | // Syntax: `let/const/var <>: <> [= value]` (Square brackets are optional, meaning we don't have to give it a value immediately unless using const) 19 | let myFirstName: string; 20 | let myLastName: string = 'Shuff'; 21 | let myAge: number = 34; 22 | let isAdult: boolean = true; 23 | let myNull: null = null; 24 | let myUndefined: undefined = undefined; 25 | 26 | // Arrays must be given an extra parameter specifying the type inside of the array 27 | let myArrayOfNums: Array = [1, 10, 20, 30]; 28 | // The above line can also be done like this: 29 | let mySecondArrayOfNums: number[] = [5, 10, 15, 20]; 30 | // String array 31 | let myArrayOfStrings: Array = ['John', 'Doe']; 32 | // Can also be done: 33 | let anotherArrayOfStrings: string[] = ['Mike', 'Shuff']; 34 | let myAnyArray: Array = [12, true, null, 'test']; 35 | let mySecondAnyArray: any[] = [true, null, 123131]; 36 | 37 | // Is a shape that want our object to conform to 38 | interface MyUserObject { 39 | name: string; 40 | age: number; 41 | } 42 | 43 | let myUser: MyUserObject = { 44 | name: 'Mike', 45 | age: 33, 46 | }; 47 | 48 | interface Window { 49 | myNumberProperty: (a: number, b: number) => number; 50 | } 51 | 52 | // Arrays 53 | let myUsers: MyUserObject[] = [myUser, myUser]; 54 | let myNums: number[] = [1, 6, 20]; 55 | let myWords: string[] = ['my', 'words']; 56 | let myChecks: boolean[] = [true, false, false, false, true]; 57 | let myObjs: object[] = [ 58 | new Date(), 59 | new Date(), 60 | new Date(), 61 | new Date(), 62 | new Date(), 63 | new Date(), 64 | {}, 65 | ]; 66 | let myNulls: null[] = [null, null]; 67 | let myUndefineds: undefined[] = [undefined, undefined, undefined]; 68 | let myArrayOfArrays: number[][] = [[1, 4, 10], [10, 10, 30], [30]]; 69 | let myAnyArray: any[] = ['test', 12, true, 'me', null, 'out']; 70 | let myDateArray: Date[] = [new Date(), new Date()]; 71 | 72 | // Arrays (alternative) 73 | let myAltUsers: Array = [myUser, myUser]; 74 | let myAltNums: Array = [1, 5, 10]; 75 | let myAltWords: Array = ['test', 'me', 'out']; 76 | let myAltChecks: Array = [true, true, false]; 77 | let myAltObjects: Array = [new Date(), new Date()]; 78 | let myAltNulls: Array = [null, null, null]; 79 | let myAltUndefined: Array = [undefined, undefined, undefined]; 80 | let myAltAny: Array = [ 81 | 'test', 82 | 12, 83 | true, 84 | null, 85 | undefined, 86 | 'me', 87 | ['1', '3'], 88 | ]; 89 | let myAltArrayOfArrays: Array> = [ 90 | [1, 2, 3], 91 | [4, 5, 6], 92 | ]; 93 | 94 | // You can declare a variable of dynamic type in TypeScript with the `any` type 95 | let myDynamicVariable: any = 'Yo'; 96 | myDynamicVariable = 12; 97 | let myDynamicArray: Array = [ 98 | 'I', 99 | 'should', 100 | 12, 101 | 'probably', 102 | null, 103 | 'avoid', 104 | 'this', 105 | ]; 106 | // Can also be done 107 | let anotherDynamicArray: any[] = ['Test', 12, true]; 108 | 109 | // Object declaration 110 | let myObject: Object = { 111 | name: 'Yo', 112 | age: 33, 113 | isAdult: true, 114 | }; 115 | 116 | // // No can do: 117 | // myObject.name = 12; 118 | console.log(myObject); 119 | 120 | // There are a few additions types that TypeScript offers: 121 | // `any` - Allow anything 122 | // `unknown` - Ensure someone using this type declares what the type is 123 | // `never` - It's not possible that this type can happen 124 | // `void` - A function which returns `undefined` or has no return value 125 | 126 | let myArray: any[] = []; 127 | 128 | function populateArray(): void { 129 | myArray.push(12); 130 | } 131 | console.log(populateArray()); 132 | // You’ll see that there are two syntaxes for building types: Interfaces and Types. You should prefer `interface`. Use `type` when you need specific features. 133 | 134 | // Exporting 135 | export = { 136 | myObject, 137 | myAge, 138 | }; 139 | -------------------------------------------------------------------------------- /workshop-1/02-interfaces.ts: -------------------------------------------------------------------------------- 1 | // You can explicitly describe this object’s shape using an interface declaration: 2 | export interface User { 3 | name: string; 4 | age: number; 5 | // We can also have Optional Properties: | = or 6 | id?: number | string; 7 | } 8 | 9 | // You can then declare that a JavaScript object conforms to the shape of your new interface by using syntax like : TypeName after a variable declaration: 10 | const myUser: User = { 11 | name: 'Michael', 12 | age: 33, 13 | id: 1231501231, 14 | }; 15 | console.log(myUser); 16 | 17 | // // If you provide an object that doesn’t match the interface you have provided, TypeScript will warn you: 18 | // const user: User = { 19 | // username: "Hayes", 20 | // id: 0, 21 | // }; 22 | 23 | // Since JavaScript supports classes and object-oriented programming, so does TypeScript. You can use an interface declaration with classes: 24 | class UserAccount { 25 | // Notice the user of semicolons rather than commas 26 | name: string; 27 | age: number; 28 | id?: number; 29 | 30 | constructor(name: string, age: number, id?: number) { 31 | this.name = name; 32 | this.age = age; 33 | this.id = id; 34 | } 35 | } 36 | 37 | // export interface User { 38 | // name: string; 39 | // age: number; 40 | // // We can also have Optional Properties: | = or 41 | // id?: number | string; 42 | // } 43 | 44 | const user: User = new UserAccount('Michael', 33); 45 | console.log(user); 46 | 47 | // You can use interfaces to annotate parameters and return values to functions: 48 | // This function MUST return a user 49 | function getAdminUser(): User { 50 | return user; 51 | } 52 | console.log(getAdminUser()); 53 | 54 | function add(a: string, b: number) { 55 | return a + b; 56 | } 57 | 58 | add('12', 12); 59 | 60 | function sayHello(): void { 61 | console.log('Hello'); 62 | } 63 | 64 | // This function MUST take in a user 65 | function deleteUser(user: User) { 66 | console.log(user, 'deleted'); 67 | } 68 | deleteUser({ name: 'test', age: 12 }); 69 | -------------------------------------------------------------------------------- /workshop-1/03-unknown.ts: -------------------------------------------------------------------------------- 1 | /**************************************** UNKNOWN *******************************************/ 2 | // Unknown is one of those types that once it clicks, you 3 | // can find quite a lot of uses for it. It acts like a sibling 4 | // to the `any` type. Where `any` allows for ambiguity - `unknown` 5 | // requires specifics. 6 | 7 | // A good example would be in wrapping a JSON parser. JSON 8 | // data can come in many different forms and the creator 9 | // of the json parsing function won't know the shape of the 10 | // data - the person calling that function should. 11 | 12 | const jsonParser = (jsonString: string) => JSON.parse(jsonString); 13 | 14 | const myAccount = jsonParser(`{ "name": "Dorothea" }`); 15 | 16 | console.log(myAccount.name); // Dorothea 17 | console.log(myAccount.email); // undefined 18 | console.log(myAccount); // { name: 'Dorothea' } 19 | 20 | // If you hover on jsonParser, you can see that it has the 21 | // return type of any, so then does myAccount. It's possible 22 | // to fix this with Generics - but it's also possible to fix 23 | // this with unknown. 24 | 25 | const jsonParserUnknown = (jsonString: string): unknown => 26 | JSON.parse(jsonString); 27 | 28 | // const myOtherAccount = jsonParserUnknown(`{ "name": "Samuel" }`); 29 | 30 | // myOtherAccount.name; 31 | 32 | // The object myOtherAccount cannot be used until the type has 33 | // been declared to TypeScript. This can be used to ensure 34 | // that API consumers think about their typing up-front: 35 | 36 | type UnknownUser = { 37 | name: string; 38 | age: string; 39 | }; 40 | const myUserAccount = jsonParserUnknown( 41 | `{ "name": "Samuel", "age": "12" }` 42 | ) as UnknownUser; 43 | console.log(myUserAccount.name); 44 | console.log(myUserAccount.age); 45 | 46 | // Unknown is a great tool, to understand it more read these: 47 | // https://mariusschulz.com/blog/the-unknown-type-in-typescript 48 | // https://www.typescriptlang.org/docs/handbook/release-notes/typescript-3-0.html#new-unknown-top-type 49 | -------------------------------------------------------------------------------- /workshop-1/04-never.ts: -------------------------------------------------------------------------------- 1 | import { User } from './02-interfaces'; 2 | // Never 3 | 4 | // Because TypeScript supports code flow analysis, the language 5 | // needs to be able to represent when code logically cannot 6 | // happen. For example, this function cannot return: 7 | 8 | const neverReturns = () => { 9 | // If it throws on the first line 10 | throw new Error('Always throws, never returns'); 11 | }; 12 | 13 | // If you hover on the type, you see it is a () => never 14 | // which means it should never happen. These can still be 15 | // passed around like other values: 16 | 17 | // const myValue = neverReturns(); 18 | 19 | // Having a function never return can be useful when dealing 20 | // with the unpredictability of the JavaScript runtime and 21 | // API consumers that might not be using types: 22 | 23 | const validateUser = (user: User) => { 24 | if (user) { 25 | return user.name !== 'NaN'; 26 | } 27 | 28 | // According to the type system, this code path can never 29 | // happen, which matches the return type of neverReturns. 30 | neverReturns(); 31 | }; 32 | 33 | const newUser: User = { 34 | name: 'Johnathan', 35 | age: 55, 36 | }; 37 | 38 | console.log(validateUser(newUser)); 39 | 40 | // The type definitions state that a user has to be passed in, 41 | // but there are enough escape valves in JavaScript whereby 42 | // you can't guarantee that. 43 | 44 | // Using a function which returns never allows you to add 45 | // additional code in places which should not be possible. 46 | // This is useful for presenting better error messages, 47 | // or closing resources like files or loops. 48 | 49 | // A very popular use for never, is to ensure that a 50 | // switch is exhaustive. E.g., that every path is covered. 51 | 52 | // Here's an enum and an exhaustive switch, try adding 53 | // a new option to the enum (maybe Tulip?) 54 | 55 | enum Flower { 56 | Rose, 57 | Rhododendron, 58 | Violet, 59 | Daisy, 60 | Tulip, 61 | Lavender, 62 | } 63 | 64 | const flowerLatinName = (flower: Flower) => { 65 | switch (flower) { 66 | case Flower.Rose: 67 | return 'Rosa rubiginosa'; 68 | case Flower.Rhododendron: 69 | return 'Rhododendron ferrugineum'; 70 | case Flower.Violet: 71 | return 'Viola reichenbachiana'; 72 | case Flower.Daisy: 73 | return 'Bellis perennis'; 74 | case Flower.Tulip: 75 | return 'blah'; 76 | case Flower.Lavender: 77 | return 'hi'; 78 | 79 | default: 80 | const _exhaustiveCheck: never = flower; 81 | return _exhaustiveCheck; 82 | } 83 | }; 84 | 85 | // Never in Unions 86 | 87 | // A never is something which is automatically removed from 88 | // a type union. 89 | 90 | type NeverIsRemoved = string | never | number; 91 | 92 | // If you look at the type for NeverIsRemoved, you see that 93 | // it is string | number. This is because it should never 94 | // happen at runtime because you cannot assign to it. 95 | -------------------------------------------------------------------------------- /workshop-1/05-enums.ts: -------------------------------------------------------------------------------- 1 | /* 2 | Enums are one of the few features TypeScript has which is not a type-level extension of JavaScript. 3 | 4 | Enums allow a developer to define a set of named constants. Using enums can make it easier to document intent, or create a set of distinct cases. TypeScript provides both numeric and string-based enums. 5 | */ 6 | 7 | /********************************** Numeric enums *********************************************/ 8 | enum Direction { 9 | Up = 1, 10 | Down, // 2 11 | Left, // 3 12 | Right, // 4 13 | } 14 | 15 | // Above, we have a numeric enum where Up is initialized with 1. All of the following members are auto - incremented from that point on.In other words, Direction.Up has the value 1, Down has 2, Left has 3, and Right has 4.; 16 | // If we didn't initialize our first enum option with `1`, it would have started at `0` 17 | // console.log(Direction); 18 | // console.log(Direction.Up); 19 | // console.log(Direction[ '1' ]); 20 | console.log(Direction['3']); 21 | 22 | // // Using an enum is simple: just access any member as a property off of the enum itself, and declare types using the name of the enum: 23 | enum UserResponse { 24 | No = 0, 25 | Yes = 1, 26 | } 27 | console.log(UserResponse['0']); 28 | 29 | function respond(recipient: string, message: UserResponse): void { 30 | console.log(`${recipient}: ${message}`); 31 | } 32 | respond('Michael', UserResponse.No); 33 | 34 | // /********************************** String enums *********************************************/ 35 | // // String enums are a similar concept, but have some subtle runtime differences as documented below. In a string enum, each member has to be constant-initialized with a string literal, or with another string enum member. 36 | enum StringDirections { 37 | Up = 'NORTH', 38 | Down = 'SOUTH', 39 | Left = 'WEST', 40 | Right = 'EAST', 41 | } 42 | 43 | console.log(StringDirections); 44 | -------------------------------------------------------------------------------- /workshop-1/06-types.ts: -------------------------------------------------------------------------------- 1 | // With TypeScript, you can create complex types by combining simple ones. There are two popular ways to do so: with `Unions`, and with `Generics`. 2 | 3 | /**************************************** Unions **************************************************/ 4 | // With a union, you can declare that a type could be one of many types. For example, you can describe a boolean type as being either true or false: 5 | 6 | type MyBoolean = true | false; 7 | // If you hover over MyBoolean above, you’ll see that it is classed as boolean. That’s a property of the Structural Type System. More on this below. 8 | 9 | // A popular use-case for union types is to describe the set of string or number literals that a value is allowed to be: 10 | type WindowStates = 'open' | 'closed' | 'minimized'; 11 | type LockStates = 'locked' | 'unlocked'; 12 | type PositiveOddNumbersUnderTen = 1 | 3 | 5 | 7 | 9; 13 | type myCrazyType = 'Hi' | false | 10; 14 | 15 | type StringOrNumber = string | number; 16 | 17 | function doStuff(myParameter: StringOrNumber) { 18 | if (typeof myParameter === 'string') { 19 | myParameter.toUpperCase(); 20 | } else { 21 | myParameter; 22 | } 23 | } 24 | 25 | // Unions provide a way to handle different types too. For example, you may have a function that takes an array or a string: 26 | function getLength(obj: string | string[]) { 27 | return `${typeof obj === 'string' ? 'string' : 'array'}: ${obj.length}`; 28 | } 29 | 30 | console.log(getLength('test')); 31 | console.log(getLength(['test', 'array'])); 32 | 33 | /**************************************** Generics ************************************************/ 34 | // Generics provide variables to types. A common example is an array. An array without generics could contain anything. An array with generics can describe the values that the array contains. 35 | 36 | type StringArray = Array; // string[] 37 | type NumberArray = Array; // number[] 38 | type ObjectArray = Array; // Object[] 39 | type ObjectWithNameArray = Array<{ name: string }>; 40 | 41 | // // You can declare your own types that use generics: 42 | interface Backpack { 43 | add: (obj: Type) => void; 44 | get: () => Type; 45 | } 46 | 47 | // // This line is a shortcut to tell TypeScript there is a 48 | // // constant called `backpack`, and to not worry about where it came from. 49 | let backpack: Backpack = { 50 | add: (string) => { 51 | console.log(`${string} added!`); 52 | }, 53 | get: () => { 54 | return 'test'; 55 | }, 56 | }; 57 | 58 | let objectBackpack: Backpack = { 59 | add: (object) => { 60 | console.log(object, `added to backpack`); 61 | }, 62 | get: () => { 63 | return {}; 64 | }, 65 | }; 66 | 67 | // // object is a string, because we declared it above as the variable part of Backpack. 68 | // // const object = backpack.get(); 69 | 70 | // // Since the backpack variable is a string, you can't pass a number to the add function. 71 | // backpack.add(12) 72 | backpack.add('Yo'); 73 | console.log(backpack); 74 | 75 | // /*********************************** Explicit Types (cont'd) ************************************/ 76 | // // More explicit types 77 | 78 | function greetUser(user: string, date: Date) { 79 | console.log(`Hello ${user}, today is ${date.toDateString()}!`); 80 | } 81 | greetUser('Michael', new Date()); 82 | 83 | // // What we did was add type annotations on person and date to describe what types of values greet can be called with. You can read that signature as ”greet takes a person of type string, and a date of type Date“. 84 | 85 | // /************************************* Erased Types ***********************************************/ 86 | -------------------------------------------------------------------------------- /workshop-1/07-structural-type-system.ts: -------------------------------------------------------------------------------- 1 | /* 2 | One of TypeScript’s core principles is that type checking focuses on the shape that values have. This is sometimes called “duck typing” or “structural typing”. 3 | 4 | In a structural type system, if two objects have the same shape, they are considered to be of the same type. 5 | */ 6 | 7 | interface Point { 8 | x: number; 9 | y: number; 10 | } 11 | 12 | function logPoint(point: Point) { 13 | console.log(`${point.x}, ${point.y}`); 14 | } 15 | 16 | const point = { x: 12, y: 26, z: 100 }; 17 | logPoint(point); 18 | 19 | // The point variable is never declared to be a Point type. However, TypeScript compares the shape of point to the shape of Point in the type-check. They have the same shape, so the code passes. 20 | 21 | // The shape - matching only requires a subset of the object’s fields to match. 22 | const point3 = { x: 12, y: 30, z: 3252 }; 23 | logPoint(point3); 24 | 25 | const rect = { x: 35, y: 404, height: 10, width: 12 }; 26 | logPoint(rect); 27 | 28 | // const color = { hex: '#FF0AC1' }; 29 | // logPoint(color); 30 | 31 | // There is no difference between how classes and objects conform to shapes: 32 | class VirtualPoint { 33 | x: number; 34 | y: number; 35 | 36 | constructor(x: number, y: number) { 37 | this.x = x; 38 | this.y = y; 39 | } 40 | } 41 | 42 | const newVirtualPoint = new VirtualPoint(15, 2000); 43 | logPoint(newVirtualPoint); 44 | -------------------------------------------------------------------------------- /workshop-1/08-index-signatures.ts: -------------------------------------------------------------------------------- 1 | // Index Signatures 2 | /* 3 | Sometimes we need to represent a type for dictionaries, where values of a consistent type are retrievable by keys. 4 | 5 | Consider this collection of phone numbers: 6 | */ 7 | const phones = { 8 | home: { country: '+1', area: '830', number: '210-0910' }, 9 | work: { country: '+1', area: '830', number: '511-0910' }, 10 | mobile: { country: '+1', area: '830', number: '630-0910' }, 11 | fax: { country: '+1', area: '830', number: '32423525' }, 12 | }; 13 | 14 | // We can store phone numbers under a `key` (home, work, mobile, etc...) where each number has three properties that are strings 15 | // We can describe this value using an `index signature` 16 | const phonesIndex: { 17 | [key: string]: // home, work, mobile, banana, potato 18 | { 19 | country: string; 20 | area: string; 21 | number: string; 22 | }; // A little bit stricter 23 | } = {}; 24 | 25 | console.log('test'); 26 | console.log(phonesIndex.area.number); 27 | phonesIndex.whatever.number; 28 | -------------------------------------------------------------------------------- /workshop-1/README.md: -------------------------------------------------------------------------------- 1 | # TypeScript 2 | 3 | TypeScript offers a strict-type check as a superset of the JavaScript language. Limiting the use of dynamic variables can help reduce unexpected behaviors and bugs. Because if is a superset of JavaScript, JavaScript syntax is legal TypeScript. 4 | 5 | ## `tsc`, the TypeScript Compiler [Code Example](./compiling/hello.ts) 6 | 7 | - Installation: 8 | - `npm install -g typescript` 9 | - This installs TypeScript globally, but feel free to install it locally in your project or even using `npx` 10 | - may need to install using `sudo` 11 | - `tsc --init` to create a `tsconfig.json` file 12 | - Any flag listed here can be configured in the `tsconfig.json` 13 | - Compiling Code: 14 | - `tsc <>` - To compile your TypeScript into JavaScript 15 | - If you have a TypeScript error and attempt to compile it, you will get a warning, but it will STILL compile it. 16 | - `tsc --noEmitOnError <>` - JavaScript file with not be updated if an error in TypeScript occurs 17 | - You may notice that `tsc` will downgrade (known as _downleveling_) your ES6 syntax (`let`, `const`, _template strings_) to ES3. without additional flags. 18 | - To keep ES6 syntax use the flag `tsc --target es2015 <>` 19 | - You can have the `tsc` compiler watch your directory, and any time a file is changed and saved, it will compile to JS: 20 | - `"dev": "tsc --target es2019 (or any other) --watch --preserveWatchOutput"` 21 | 22 | ### Strictness 23 | 24 | - Different users use TypeScript for different reasons. Some are looking for a more lenient experience where they can use _type inference_ and there is no checking for potential `null`/`undefined` values. (This is the best way to start when migrating JavaScript files) 25 | - Many users prefer to have TypeScript validate as much as it can immediately, which is why TypeScript offers Strictness settings. These strictness settings turn static type-checking from a switch (either your code is checked or not) into something closer to a dial. The further you turn this dial up, the more TypeScript will check for you. This can require a little extra work, but generally speaking it pays for itself in the long run, and enables more thorough checks and more accurate tooling. When possible, a new codebase should always turn these strictness checks on. 26 | - TypeScript has several type-checking strictness flags that can be turned on or off: 27 | - The `--strict` flag in the CLI, or `"strict": true` in a `tsconfig.json` toggles them all on simultaneously, but we can opt out of them individually. The two biggest ones you should know about are `noImplicitAny` and `strictNullChecks`. 28 | - **noImplicitAny** 29 | - Recall that in some places, TypeScript doesn’t try to infer any types for us and instead falls back to the most lenient type: `any`. This isn’t the worst thing that can happen - after all, falling back to `any` is just the plain JavaScript experience anyway. 30 | - However, using `any` often defeats the purpose of using TypeScript. The more typed your program is, the more validation and tooling you'll get, allowing you to avoid more bugs. 31 | - Turning on the `noImplicitAny` flag will issue an error on any variables whose type is implicitly inferred as `any`. 32 | - **strictNullChecks** 33 | - By default, values like `null` and `undefined` are assignable to any other type. This can make writing some code easier, but forgetting to handle `null` and `undefined` is the cause of countless bugs in the world - some consider it a billion dollar mistake! The `strictNullChecks` flag makes handling `null` and `undefined` more explicit, and spares us from worrying about whether we forgot to handle `null` and `undefined`. 34 | 35 | ## ts-node - Code Runner for TypeScript 36 | 37 | - You can run TypeScript code without compiling with the TypeScript compiler by installing `ts-node` either globally or for your project 38 | - `npm install ts-node` 39 | - For Windows, `ctrl + alt + n` will run the TypeScript code for you 40 | -------------------------------------------------------------------------------- /workshop-1/compiling/hello.js: -------------------------------------------------------------------------------- 1 | // Yeee 2 | console.log("Hello, world!"); 3 | // // This is an industrial-grade general-purpose greeter function 4 | // function greet(person, date) { 5 | // console.log(`Hello ${person}! Today is ${date}`); 6 | // } 7 | // greet("John"); 8 | function greet(user, date) { 9 | console.log(`Hello ${user}, today is ${date.toDateString()}!`); 10 | } 11 | greet("Michael", new Date()); 12 | -------------------------------------------------------------------------------- /workshop-1/compiling/hello.ts: -------------------------------------------------------------------------------- 1 | // Yeee 2 | console.log("Hello, world!"); 3 | 4 | // // This is an industrial-grade general-purpose greeter function 5 | // function greet(person, date) { 6 | // console.log(`Hello ${person}! Today is ${date}`); 7 | // } 8 | 9 | // greet("John"); 10 | 11 | 12 | function greet(user: string, date: Date) { 13 | console.log(`Hello ${user}, today is ${date.toDateString()}!`); 14 | } 15 | greet("Michael", new Date()); 16 | -------------------------------------------------------------------------------- /workshop-2/01-everyday-types-01.ts: -------------------------------------------------------------------------------- 1 | // TypeScript will automatically infer a type if possible 2 | let myName = 'Michael Shuff'; 3 | 4 | // Union 5 | type StringOrNum = string | number; 6 | 7 | // Type Annotations 8 | let myString: string = 'Hello world'; 9 | let myStringOrNumber: StringOrNum = 15; 10 | myStringOrNumber = 'Yo'; 11 | let myNumber: number = 25; 12 | let myBool: boolean = true; 13 | let numArray: number[] = [1, 5, 10]; // OR Array 14 | let mySecondNumArray: Array = [3, 6, 7]; 15 | let stringArray: string[] = ['Hello', 'World']; // OR Array 16 | let booleanArray: boolean[] = [true, false, false, true]; // OR Array 17 | let anyArray: any[] = ["don't", 'do', true, 'this']; // OR Array 18 | let stringOrNumArray: StringOrNum[] = [1, 'yo']; 19 | 20 | // When a value is of type any, you can access any properties of it (which will in turn be of type any), call it like a function, assign it to (or from) a value of any type, or pretty much anything else that’s syntactically legal: 21 | // let myObj: any = { x: 0 }; 22 | 23 | // myObj.foo(); 24 | // myObj(); 25 | // myObj.bar = 100; 26 | // myObj = 'hello world'; 27 | // const n: number = myObj; 28 | 29 | // The any type is useful when you don’t want to write out a long type just to convince TypeScript that a particular line of code is okay. 30 | // When you don’t specify a type, and TypeScript can’t infer it from context, the compiler will typically default to any. You usually want to avoid this, though, because any isn’t type - checked. Use the compiler flag noImplicitAny to flag any implicit any as an error. 31 | 32 | // While you don't need to use type annotations for the most part due to TypeScript being able to infer the type you're creating, it is useful to add annotations to either the parameter or return type of a function 33 | // person must be a string 34 | function sayHi(person: string) { 35 | console.log(`Hi, ${person}!`); 36 | } 37 | sayHi('John'); 38 | 39 | // function addTwo(a: string, b: string) { 40 | // return a + b; 41 | // } 42 | 43 | // It is not extremely necessary to specify the return type in an annotation, as it can usually be inferred by looking at what you return 44 | // function must return a number 45 | // Type Driven Development - The idea of creating a function with an explicit return type and adding it to your code before implementing the actual functionality 46 | function getRandomNumber(): number { 47 | return Math.random() * 100; 48 | // return 'hi' 49 | } 50 | 51 | // Anonymous Functions 52 | // Anonymous functions are a little bit different from function declarations. When a function appears in a place where TypeScript can determine how it’s going to be called, the parameters of that function are automatically given types. 53 | 54 | const names: string[] = ['Mike', 'Mark', 'Allie']; 55 | 56 | // // Contextual Typing 57 | // names.forEach(function (name) { 58 | // console.log(name.toUppercase()); 59 | // }) 60 | 61 | // Contextual Typing also works for arrow functions 62 | // names.forEach((name) => { 63 | // console.log(name.toUppercase()); 64 | // }) 65 | 66 | // Object Types 67 | /* 68 | Apart from primitives, the most common sort of type you’ll encounter is an object type. This refers to any JavaScript value with properties, which is almost all of them! To define an object type, we simply list its properties and their types. 69 | 70 | For example, here’s a function that takes a point-like object: 71 | */ 72 | // export function printCoordinates(pt: { x: number, y: number; }): void { 73 | // console.log(`x: ${pt.x}, y: ${pt.y}`); 74 | // } 75 | // printCoordinates({ x: 12, y: 15 }); 76 | 77 | // // You can also separate properties with a semicolon(;) 78 | // function printCoordinates(pt: { x: number; y: number; }): void { 79 | // console.log(`x: ${pt.x}, y: ${pt.y}`); 80 | // } 81 | 82 | // Optional Properties 83 | // Object types can also specify that some or all of their properties are optional. To do this, add a ? after the property name: 84 | function printName(obj: { first: string; last?: string }): void { 85 | // Note how you HAVE to check if the optional property was defined or not. 86 | console.log(`${obj.last ? `${obj.first} ${obj.last}` : `${obj.first}`}`); 87 | } 88 | printName({ first: 'Michael', last: 'Shuff' }); 89 | printName({ first: 'Michael' }); 90 | 91 | // Union Types 92 | // The first way to combine types you might see is a union type. A union type is a type formed from two or more other types, representing values that may be any one of those types. We refer to each of these types as the union’s members. 93 | function printID(id: string | number): void { 94 | console.log(`Your ID: ${id}`); 95 | } 96 | printID(100); 97 | printID('100'); 98 | 99 | // Using typeof with union types 100 | // You often need to use typeof to determine what course of action to follow when using union types 101 | function welcomePeople(x: string[] | string): void { 102 | Array.isArray(x) 103 | ? console.log(`Hello, ${x.join(' and ')}`) 104 | : console.log(`Welcome, ${x}`); 105 | } 106 | welcomePeople(['John', 'Mike', 'Warren', 'Cole']); 107 | welcomePeople('John and Joe'); 108 | 109 | // Sometimes you’ll have a union where all the members have something in common. For example, both arrays and strings have a slice method. If every member in a union has a property in common, you can use that property without narrowing: 110 | function getFirstThree(x: number[] | string) { 111 | return x.slice(0, 3); 112 | } 113 | console.log(getFirstThree([1, 5, 10, 20, 30])); 114 | console.log(getFirstThree('Joseph')); 115 | -------------------------------------------------------------------------------- /workshop-2/02-everyday-types-02.ts: -------------------------------------------------------------------------------- 1 | // Type Aliases 2 | /* 3 | We’ve been using object types and union types by writing them directly in type annotations. This is convenient, but it’s common to want to use the same type more than once and refer to it by a single name. 4 | 5 | A type alias is exactly that - a name for any type. The syntax for a type alias is: 6 | */ 7 | 8 | // You can use semi-colons (;) or commas (,) to separate properties on an type 9 | // It is convention to use TitleCase for alias names and interface names 10 | type Point = { 11 | x: number; 12 | y: number; 13 | }; 14 | 15 | // interface PointType { 16 | // x: number; 17 | // y: number; 18 | // } 19 | 20 | let myPoint: Point = { 21 | x: 150, 22 | y: 200, 23 | }; 24 | 25 | function myCoords(pt: Point): void { 26 | console.log(`${pt.x}, ${pt.y}`); 27 | } 28 | 29 | myCoords(myPoint); 30 | let testPoint = { x: 234, y: 3242, z: -5 }; 31 | myCoords({ x: 234, y: 3242 }); 32 | 33 | // You can actually use a type alias to give a name to any type at all, not just an object type. For example, a type alias can name a union type: 34 | type ID = string | number; 35 | 36 | // Note that aliases are only aliases - you cannot use type aliases to create different/distinct “versions” of the same type. When you use the alias, it’s exactly as if you had written the aliased type. In other words, this code might look illegal, but is OK according to TypeScript because both types are aliases for the same type: 37 | type TrimmedString = string; 38 | 39 | function trimInput(str: string): TrimmedString { 40 | return str.trimStart().trimEnd(); 41 | } 42 | 43 | let userInput = trimInput(' Jonathan '); 44 | console.log(userInput); 45 | userInput = 'January'; 46 | 47 | // Interfaces 48 | // An interface declaration is another way to name an object type: 49 | interface PointInterface { 50 | x: number; 51 | y: number; 52 | } 53 | 54 | // Type aliases and interfaces are very similar, and in many cases you can choose between them freely. Almost all features of an interface are available in type, the key distinction is that a type cannot be re-opened to add new properties vs an interface which is always extendable. 55 | 56 | // Interface Extension 57 | // interface Animal { 58 | // name: string; 59 | // } 60 | 61 | // interface Bear extends Animal { 62 | // honey: boolean; 63 | // } 64 | 65 | // let myBear: Bear = { 66 | // honey: true, 67 | // name: 'Yogi', 68 | // }; 69 | 70 | // Type Extension 71 | // type Animal = { 72 | // name: string; 73 | // }; 74 | 75 | // type Bear = Animal & { 76 | // honey: boolean; 77 | // }; 78 | 79 | // let myBear: Bear = { 80 | // name: 'BooBoo', 81 | // honey: true, 82 | // }; 83 | 84 | // Adding new fields to type and interface 85 | // Interface 86 | // interface MyWindow { 87 | // title: string; 88 | // } 89 | 90 | // interface MyWindow { 91 | // size: { height: number; width: number }; 92 | // } 93 | 94 | interface Window { 95 | myProperty: string; 96 | } 97 | 98 | interface String { 99 | myRandomProperty: number; 100 | } 101 | 102 | // interface HTMLCanvasElement { 103 | // myNum: number, 104 | // } 105 | 106 | // let myWindow: MyWindow = { 107 | // title: 'Mozilla Developer Network', 108 | // size: { height: 100, width: 100 }, 109 | // }; 110 | 111 | // // Type 112 | // type MyWindow = { 113 | // title: string, 114 | // } 115 | 116 | // type MyWindow = { 117 | // size: { height: number, width: number} 118 | // } 119 | // // Error: Duplicate identifier 'Window'. 120 | 121 | interface Window { 122 | myProperty: string; 123 | } 124 | 125 | interface Document { 126 | myNumbers: number[]; 127 | } 128 | -------------------------------------------------------------------------------- /workshop-2/03-everyday-types-03.ts: -------------------------------------------------------------------------------- 1 | // Type Assertions 2 | /* 3 | Sometimes you will have information about the type of a value that TypeScript can’t know about. 4 | 5 | For example, if you’re using document.getElementById, TypeScript only knows that this will return some kind of HTMLElement, but you might know that your page will always have an HTMLCanvasElement with a given ID. 6 | 7 | In this situation, you can use a type assertion to specify a more specific type: 8 | */ 9 | const myCanvas = document.getElementById('main-canvas') as HTMLCanvasElement; 10 | /* 11 | Like a type annotation, type assertions are removed by the compiler and won’t affect the runtime behavior of your code. 12 | 13 | You can also use the angle-bracket syntax (except if the code is in a .tsx file), which is equivalent: 14 | 15 | Reminder: Because type assertions are removed at compile-time, there is no runtime checking associated with a type assertion. There won’t be an exception or null generated if the type assertion is wrong. 16 | */ 17 | // const myCanvasAlternate = document.getElementById("main-canvas"); 18 | 19 | // TypeScript only allows type assertions which convert to a more specific or less specific version of a type. This rule prevents “impossible” coercions like: 20 | // const x = "hello" as number; 21 | 22 | // Sometimes this rule can be too conservative and will disallow more complex coercions that might be valid. If this happens, you can use two assertions, first to any (or unknown, which we’ll introduce later), then to the desired type: 23 | const a = 'hello' as any as number; 24 | 25 | // Literal Types 26 | /* 27 | In addition to the general types string and number, we can refer to specific strings and numbers in type positions. 28 | 29 | One way to think about this is to consider how JavaScript comes with different ways to declare a variable. Both var and let allow for changing what is held inside the variable, and const does not. This is reflected in how TypeScript creates types for literals. 30 | */ 31 | let changingString = 'Hello World!'; 32 | changingString = 'Pikachu'; 33 | 34 | const constantString = 'I will never change'; 35 | 36 | // By themselves, literal types aren’t very valuable: 37 | let x: 'Hello' = 'Hello'; 38 | x = 'Hello'; 39 | // x = "hello" 40 | 41 | // But by combining literals into unions, you can express a much more useful concept - for example, functions that only accept a certain set of known values: 42 | function printText(s: string, alignment: 'left' | 'right' | 'center'): void { 43 | // ... 44 | } 45 | printText('Hey friends!', 'left'); 46 | // printText("Hey friends!", "centre") 47 | 48 | function compare(a: string, b: string): -1 | 0 | 1 { 49 | return a === b ? 0 : a > b ? 1 : -1; 50 | } 51 | console.log(compare('john', 'john')); 52 | console.log(compare('john', 'mike')); 53 | console.log(compare('mike', 'john')); 54 | 55 | // Of course, you can combine these with non-literal types: 56 | interface Options { 57 | width: number; 58 | height: number; 59 | } 60 | function configure(x: Options | 'auto' = 'auto') { 61 | // ... 62 | } 63 | configure({ width: 100, height: 45343 }); 64 | configure('auto'); 65 | // We can still use '=' to set default options 66 | configure(); 67 | 68 | // Literal Interface 69 | // // When you initialize a variable with an object, TypeScript assumes that the properties of that object might change values later. For example, if you wrote code like this: 70 | // const object = { counter: 0 }; 71 | // if (object.counter === 0) object.counter++; 72 | 73 | // const req = { url: "https://www.google.com", method: "GET" }; 74 | 75 | function handleRequest(url: string, method: 'GET' | 'POST') { 76 | console.log(url); 77 | console.log(method); 78 | } 79 | 80 | /* 81 | In the above example req.method is inferred to be string, not "GET". Because code can be evaluated between the creation of req and the call of handleRequest which could assign a new string like "GUESS" to req.method, TypeScript considers this code to have an error. 82 | 83 | There are two ways to work around this. 84 | */ 85 | // 1. You can change the inference by adding a type assertion in either location: 86 | // Change 1 87 | // const req = { url: "https://www.google.com", method: "GET" as "GET"} 88 | // const req = { url: "https://www.google.com", method: "POST"} 89 | // Change 2 90 | // handleRequest(req.url, req.method as "GET" | "POST") 91 | // // Change 1 means “I intend for req.method to always have the literal type "GET"”, preventing the possible assignment of "GUESS" to that field after. Change 2 means “I know for other reasons that req.method has the value "GET"“. 92 | 93 | // // 2. You can use `as const` to convert the entire object to be type literals: 94 | const req = { url: 'https://www.google.com', method: 'GET' } as const; 95 | handleRequest(req.url, req.method); 96 | // The as const suffix acts like const but for the type system, ensuring that all properties are assigned the literal type instead of a more general version like string or number. 97 | 98 | // Tuples - A multi-element, ordered, data structure, where the position of each item has special meaning or convention 99 | 100 | // [Year, Make, Model] 101 | // let myCar = [ 2002, "Toyota", "Corolla" ]; 102 | 103 | // How TypeScript handles inference: let myCar: (string | number)[] 104 | // Be more explicit when creating a tuple 105 | let myCar: [number, string, string] = [2002, 'Toyota', 'Corolla']; 106 | 107 | // destructured assignment is convenient here: 108 | const [year, make, model] = myCar; 109 | // How TypeScript handles inference: const model: string | number 110 | 111 | // Limited Support for enforcing tuple length constraints 112 | // const numPair: [number, number] = [4, 5, 6] 113 | const numPair: [number, number] = [4, 5]; 114 | numPair.push(30); 115 | console.log(numPair); 116 | -------------------------------------------------------------------------------- /workshop-2/04-narrowing.ts: -------------------------------------------------------------------------------- 1 | /* In order to give context to narrowing, let's revisit unions 2 | - Unions are described using the `|` (pipe) operator 3 | - Unions can be thought of as an `or` clause 4 | - "success" | "error" = "success" or "error" 5 | */ 6 | function flipCoin(): 'heads' | 'tails' { 7 | if (Math.random() > 0.5) return 'heads'; 8 | return 'tails'; 9 | } 10 | 11 | /* Narrowing With Type Guards 12 | - When we need to separate the potential possibilities of a Union, we often need to do type guards 13 | - Type guards are expressions, which when used with control flow statements, allow us to have a more specific type for a particular value 14 | - Often times we will use `typeof` and `instanceof` for narrowing with type guards. 15 | */ 16 | function maybeGetUserInfo( 17 | // ?: - optional parameter 18 | string?: 'error' | 'success' 19 | ): Error | { name: string; email: string } | 'need an argument' { 20 | if (string === 'error') { 21 | throw new Error(); 22 | } else if (string === 'success') { 23 | return { name: 'Michael', email: 'mshuff@appacademy.io' }; 24 | } else { 25 | return 'need an argument'; 26 | } 27 | } 28 | 29 | console.log(maybeGetUserInfo('success')); 30 | // console.log(maybeGetUserInfo('error')); 31 | console.log(maybeGetUserInfo()); 32 | 33 | class Person { 34 | name: string; 35 | age: number; 36 | 37 | constructor(name: string, age: number) { 38 | this.name = name; 39 | this.age = age; 40 | } 41 | } 42 | 43 | const person1 = new Person('Mike', 33); 44 | 45 | function returnPerson(person: Person | { name: string; age: number }): Person { 46 | if (person instanceof Person) return person; 47 | 48 | console.log('Not an existing person, creating...'); 49 | const newPerson = new Person(person.name, person.age); 50 | return newPerson; 51 | } 52 | 53 | console.log(returnPerson(person1)); 54 | console.log(returnPerson({ name: 'John', age: 50 })); 55 | 56 | function maybeGetInfoTwo(): 57 | | ['error', Error] 58 | | ['success', { name: string; email: string }] { 59 | if (Math.random() > 0.5) { 60 | return ['error', new Error()]; 61 | } 62 | return ['success', { name: 'John', email: 'john@email.com' }]; 63 | } 64 | 65 | const outcome = maybeGetInfoTwo(); 66 | const [first, second] = outcome; 67 | 68 | if (second instanceof Error) { 69 | second; 70 | } else { 71 | second; 72 | } 73 | 74 | // Discriminated Unions ("tagged" union type) 75 | // Using a specific 'key' to know what's available while having many different options (here we only have two, but you could have more) 76 | // Can also be done with an Object 77 | const secondOutcome = maybeGetInfoTwo(); 78 | if (secondOutcome[0] === 'error') { 79 | // In this branch of code, second is an Error 80 | secondOutcome; 81 | } else { 82 | secondOutcome; 83 | } 84 | 85 | // Intersection Types 86 | // Intersection types can be described using the `&` (ampersand) operator 87 | 88 | // For example, what if we had a Promise that had extra startTime and endTime properties to it? 89 | // We can merge these types together with an intersection return 90 | function makeWeek(): { start: Date } & { end: Date } { 91 | // <- return type 92 | 93 | const start = new Date(); 94 | const end = new Date(start.valueOf() + 20000); 95 | console.dir(typeof start); 96 | return { start, end }; // kind of Object.assign. 97 | // return start; 98 | // return end; 99 | } 100 | 101 | // Here we get everything from our start Date with the addition of an end Date 102 | const thisWeek = makeWeek(); 103 | console.log(thisWeek); 104 | // thisWeek.toISOString(); 105 | thisWeek.end.toISOString(); 106 | 107 | // Union Types are FAR more common than Intersection Types, but you will see them 108 | 109 | type SpecialDate = { date: Date } & { getReason(): string }; 110 | 111 | const newYearsEve: SpecialDate = { 112 | date: new Date(), 113 | getReason: () => 'Last day of the year!', 114 | }; 115 | 116 | console.log(newYearsEve.getReason()); 117 | console.dir(newYearsEve); 118 | -------------------------------------------------------------------------------- /workshop-2/05-interfaces-implement.ts: -------------------------------------------------------------------------------- 1 | // For review, interface is perfect for coming up with shapes that an object MUST conform to 2 | interface AnimalLike { 3 | eat(food: string): void; 4 | } 5 | 6 | // // // When mixing interfaces with classes, you want to use the 'implements' keyword 7 | // class Dog implements AnimalLike { 8 | // bark() { 9 | // return 'woof'; 10 | // } 11 | 12 | // eat(food: string) { 13 | // return food + ' yum'; 14 | // } 15 | // } 16 | 17 | // let myDog = new Dog(); 18 | // console.log(myDog.bark()); 19 | // console.log(myDog.eat('food')); 20 | 21 | // // When mixing interfaces, we use 'extends' 22 | // interface Animal { 23 | // isAlive(): boolean; 24 | // } 25 | 26 | // interface Mammal extends Animal { 27 | // getFurOrHairColor(): string; 28 | // } 29 | 30 | // interface Dog extends Mammal { 31 | // getBreed(): string; 32 | // } 33 | 34 | // function sayDogBreed(dog: Dog) { 35 | // console.log(dog.getBreed()); 36 | // console.log(dog.isAlive()); 37 | // } 38 | 39 | // const myDoggo: Dog = { 40 | // isAlive: () => true, 41 | // getFurOrHairColor: () => 'black', 42 | // getBreed: () => 'german shepherd', 43 | // }; 44 | 45 | // console.log(myDoggo.isAlive()); 46 | // console.log(myDoggo.getFurOrHairColor()); 47 | // console.log(myDoggo.getBreed()); 48 | 49 | // // Although JS and TS do not support true multiple inheritance, we can emulate it by subscribing a class to multiple interfaces and extends 50 | // class LivingOrganism { 51 | // isAlive = () => { 52 | // return true; 53 | // }; 54 | // } 55 | 56 | // interface AnimalLike { 57 | // eat(food: string): void; 58 | // } 59 | 60 | // interface CanBark { 61 | // bark(): string; 62 | // } 63 | 64 | // class Dog extends LivingOrganism implements AnimalLike, CanBark { 65 | // eat = (food: string) => console.log(`Consumes ${food}`); 66 | // bark = () => `Woof Woof`; 67 | // } 68 | 69 | // Open Interfaces 70 | // TypeScript interfaces are 'open', meaning that unlike in type aliases, you can have multiple declarations in the same scope. 71 | interface AnimalLike { 72 | isAlive(): boolean; 73 | } 74 | 75 | interface AnimalLike { 76 | eat(food: string): void; 77 | } 78 | 79 | function feed(animal: AnimalLike): string { 80 | animal.eat('fooddss'); 81 | return animal.isAlive().toString(); 82 | } 83 | 84 | const myAnimal: AnimalLike = { 85 | isAlive: () => false, 86 | eat: (food: string) => console.log(`Eats ${food}`), 87 | }; 88 | 89 | myAnimal.eat('Yeeet'); 90 | 91 | console.log(feed(myAnimal)); 92 | 93 | // // You may be asking yourself: where and how is this useful? 94 | // // Imagine a situation where you want to add a global property to the window object 95 | window.document; // existing property 96 | window.exampleProperty = 42; 97 | 98 | interface Window { 99 | exampleProperty: number; 100 | } 101 | 102 | // Choosing which to use 103 | // In most situations, either one will suffice, BUT: 104 | // - If you need to define something other than an object type (e.g., use of the | union type operator), you must use a type alias 105 | // - If you need to define a type to use with the implements heritage term, it’s best to use an interface 106 | // - If you need to allow consumers of your types to augment them, you must use an interface. 107 | 108 | // Recursion 109 | // Recursive types, are self-referential, and are often used to describe infinitely nestable types. For example, consider infinitely nestable arrays of numbers: 110 | // [3, 4, [5, 6, [7], 59], 221] 111 | 112 | // You may read or see things that indicate you must use a combination of interface and type for recursive types. 113 | // - As of TypeScript 3.7, this is much easier now, and works with either type aliases or interfaces 114 | type NestedNumbers = number | NestedNumbers[]; 115 | 116 | const val: NestedNumbers = [3, 4, [5, 6, [7, [10, 20]], 59], 221]; 117 | val.push(12); 118 | console.log(val); 119 | -------------------------------------------------------------------------------- /workshop-2/06-structural-vs-nominal-types.ts: -------------------------------------------------------------------------------- 1 | // Type-checking can be thought of as a task that attempts to evaluate the question of compatibility or type equivalence 2 | // function foo(x) { 3 | // // ... 4 | // } 5 | 6 | // TYPE CHECKING 7 | // ------------- 8 | // Is `value` type-equivalent to what `foo` wants to receive? 9 | // foo(value); 10 | 11 | // Static Type Systems is code that are type systems that have type-checking before compile/runtime 12 | // TypeScript, Java, C#, C++, Scala, Haskell are all static typed 13 | 14 | // Dynamic Type Systems perform their type-checking at runtime 15 | // JavaScript, Python, Ruby, Perl, PHP are dynamically typed 16 | 17 | // Nominal vs. Structural 18 | // // Nominal Type Systems are all about NAMES. Here's a Java example: 19 | // public class Car { 20 | // String make; 21 | // String model; 22 | // int make; 23 | // } 24 | // public class CarChecker { 25 | // // Takes a `Car` argument, returns a `String` 26 | // public static String printCar(Car car) {} 27 | // } 28 | 29 | // Car myCar = new Car(); 30 | // // TYPE CHECKING 31 | // // ------------- 32 | // // Is `myCar` type-equivalent to what checkCar wants as an argument? 33 | // CarChecker.checkCar(myCar) 34 | 35 | // https://medium.com/@thejameskyle/type-systems-structural-vs-nominal-typing-explained-56511dd969f4 36 | 37 | // Structural Type Systems are all about STRUCTURE and SHAPE. Here's a TypeScript example 38 | class Car { 39 | make: string; 40 | model: string; 41 | year: number; 42 | isElectric: boolean; 43 | 44 | constructor(make: string, model: string, year: number, isElectric: boolean) { 45 | this.make = make; 46 | this.model = model; 47 | this.year = year; 48 | this.isElectric = isElectric; 49 | } 50 | } 51 | 52 | class Truck { 53 | make: string; 54 | model: string; 55 | year: number; 56 | towingCapacity: number; 57 | 58 | constructor( 59 | make: string, 60 | model: string, 61 | year: number, 62 | towingCapacity: number 63 | ) { 64 | this.make = make; 65 | this.model = model; 66 | this.year = year; 67 | this.towingCapacity = towingCapacity; 68 | } 69 | } 70 | 71 | const vehicle = { 72 | make: 'Honda', 73 | model: 'Accord', 74 | year: 2017, 75 | }; 76 | 77 | function printCar(car: { make: string; model: string; year: number }) { 78 | console.log(`${car.make}, ${car.model}, ${car.year}`); 79 | } 80 | 81 | printCar(new Car('Honda', 'Accord', 2000, false)); // Fine 82 | printCar(new Truck('Ford', 'F150', 1999, 32423)); 83 | printCar(vehicle); 84 | -------------------------------------------------------------------------------- /workshop-2/07-json-types-exercise.ts: -------------------------------------------------------------------------------- 1 | // /* JSON Types Exercise 2 | // Let’s put our knowledge to the test, by defining a type that describes any allowable JSON value. 3 | 4 | // Here’s the relevant section of the specification: 5 | // A JSON value MUST be an: 6 | // - object 7 | // - array 8 | // - number 9 | // - string, 10 | // or one of the following three literal names: 11 | // - false 12 | // - true 13 | // - null 14 | // */ 15 | 16 | // Starting Point 17 | // All of our primitive types 18 | type JSONObject = any; 19 | type JSONArray = any; 20 | type JSONValue = any; 21 | 22 | ///// DO NOT EDIT ANY CODE BELOW THIS LINE ///// 23 | function isJSON(arg: JSONValue) {} 24 | 25 | // POSITIVE test cases (must pass) 26 | isJSON('hello'); 27 | isJSON([4, 8, 15, 16, 23, 42]); 28 | isJSON([4, [8, [15, 'test']], 16, 23, 42]); 29 | isJSON([true, false, false, true]); 30 | isJSON({ greeting: { hello: true } }); 31 | isJSON(false); 32 | isJSON(true); 33 | isJSON(null); 34 | isJSON({ a: { b: [2, 3, 'foo'] } }); 35 | 36 | // NEGATIVE test cases (must fail) 37 | // @ts-expect-error 38 | isJSON(() => ''); 39 | // @ts-expect-error 40 | isJSON(class {}); 41 | // @ts-expect-error 42 | isJSON(undefined); 43 | // @ts-expect-error 44 | isJSON(new BigInt(143)); 45 | // @ts-expect-error 46 | isJSON(isJSON); 47 | -------------------------------------------------------------------------------- /workshop-2/08-functions-and-overloading.ts: -------------------------------------------------------------------------------- 1 | /* Callable Types 2 | - Both type aliases and and interfaces offer the capability to describe call signatures: 3 | */ 4 | interface TwoNumberCalculation { 5 | (x: number, y: number): number; 6 | } 7 | 8 | type TwoNumberCalc = (x: number, y: number) => number; 9 | 10 | const add: TwoNumberCalculation = (a, b) => a + b; 11 | const subtract: TwoNumberCalc = (x, y) => x - y; 12 | 13 | // Let’s pause for a minute to note: 14 | // - The return type for an interface is :number, and for the type alias it’s => number 15 | // - Because we provide types for the functions add and subtract, we don’t need to provide type annotations 16 | // for each individual function’s argument list or return type 17 | 18 | /* void 19 | Sometimes functions don’t return anything, and we know from experience with JavaScript, 20 | what actually happens in the situation below is that x will be undefined: 21 | */ 22 | function printFormattedJSON(obj: string[]) { 23 | console.log(JSON.stringify(obj, null, ' ')); 24 | } 25 | const myVar = printFormattedJSON(['hello', 'world']); 26 | 27 | // void is a special type, that’s specifically used to describe function return values. It has the following meaning: 28 | // - The return value of a void function is intended to be ignored 29 | // We could type functions as returning undefined, but there are some interesting differences that highlight the reason for void’s existence: 30 | function invokeInFourSeconds(callback: () => undefined) { 31 | setTimeout(callback, 4000); 32 | } 33 | 34 | function invokeInFiveSeconds(callback: () => void) { 35 | setTimeout(callback, 5000); 36 | } 37 | 38 | const values: number[] = []; 39 | 40 | // It happens that Array.prototype.push returns a number, and our invokeInFourSeconds function above is unhappy about this being returned from the callback. 41 | // invokeInFourSeconds(() => values.push(4)); 42 | invokeInFiveSeconds(() => values.push(5)); 43 | 44 | /* Construct Signatures 45 | - Construct signatures are similar to call signatures, except they describe what should happen with the new keyword. 46 | - These are rare, but if you ever happen to come across them - you now know what they are. 47 | */ 48 | interface DateConstructor { 49 | new (value: number): Date; 50 | } 51 | 52 | let myDateConstructor: DateConstructor = Date; 53 | 54 | const d = new myDateConstructor(3241212); 55 | 56 | /* Function Overloads 57 | 58 | - Imagine the following situation: 59 |