├── .gitignore
├── README.md
├── lesson01
├── build
│ ├── index.html
│ └── js
│ │ └── main.js
├── src
│ └── main.ts
└── tsconfig.json
├── lesson02
├── build
│ ├── index.html
│ └── js
│ │ └── main.js
├── src
│ └── main.ts
└── tsconfig.json
├── lesson03
├── build
│ ├── index.html
│ └── js
│ │ └── main.js
├── src
│ └── main.ts
└── tsconfig.json
├── lesson04
├── build
│ ├── index.html
│ └── js
│ │ └── main.js
├── src
│ └── main.ts
└── tsconfig.json
├── lesson05
├── build
│ ├── index.html
│ └── js
│ │ ├── copyright.js
│ │ └── main.js
├── src
│ ├── copyright.ts
│ └── main.ts
└── tsconfig.json
├── lesson06
├── build
│ ├── index.html
│ └── js
│ │ └── main.js
├── src
│ └── main.ts
└── tsconfig.json
├── lesson07
├── build
│ ├── index.html
│ └── js
│ │ └── main.js
├── src
│ └── main.ts
└── tsconfig.json
├── lesson08
├── build
│ ├── index.html
│ └── js
│ │ └── main.js
├── src
│ └── main.ts
└── tsconfig.json
├── lesson09
├── build
│ ├── index.html
│ └── js
│ │ └── main.js
├── src
│ └── main.ts
└── tsconfig.json
├── lesson11
├── .gitignore
├── index.html
├── package-lock.json
├── package.json
├── src
│ ├── css
│ │ └── style.css
│ ├── main.ts
│ ├── model
│ │ ├── FullList.ts
│ │ └── ListItem.ts
│ ├── templates
│ │ └── ListTemplate.ts
│ └── vite-env.d.ts
└── tsconfig.json
├── lesson12
├── .gitignore
├── index.html
├── package-lock.json
├── package.json
├── public
│ └── vite.svg
├── src
│ ├── App.tsx
│ ├── assets
│ │ └── react.svg
│ ├── components
│ │ ├── Counter.tsx
│ │ ├── Heading.tsx
│ │ ├── List.tsx
│ │ └── Section.tsx
│ ├── index.css
│ ├── main.tsx
│ └── vite-env.d.ts
├── tsconfig.json
├── tsconfig.node.json
└── vite.config.ts
├── lesson13
├── .gitignore
├── index.html
├── package-lock.json
├── package.json
├── public
│ └── vite.svg
├── src
│ ├── App.tsx
│ ├── assets
│ │ └── react.svg
│ ├── index.css
│ ├── main.tsx
│ └── vite-env.d.ts
├── tsconfig.json
├── tsconfig.node.json
└── vite.config.ts
├── lesson14
├── .gitignore
├── index.html
├── package-lock.json
├── package.json
├── public
│ └── vite.svg
├── src
│ ├── App.tsx
│ ├── Counter.tsx
│ ├── assets
│ │ └── react.svg
│ ├── index.css
│ ├── main.tsx
│ └── vite-env.d.ts
├── tsconfig.json
├── tsconfig.node.json
└── vite.config.ts
├── lesson15
├── .gitignore
├── index.html
├── package-lock.json
├── package.json
├── public
│ └── vite.svg
├── src
│ ├── App.tsx
│ ├── Counter.tsx
│ ├── assets
│ │ └── react.svg
│ ├── context
│ │ └── CounterContext.tsx
│ ├── index.css
│ ├── main.tsx
│ └── vite-env.d.ts
├── tsconfig.json
├── tsconfig.node.json
└── vite.config.ts
├── lesson16
├── .gitignore
├── data
│ └── products.json
├── index.html
├── package-lock.json
├── package.json
├── public
│ └── vite.svg
├── src
│ ├── App.tsx
│ ├── assets
│ │ └── react.svg
│ ├── context
│ │ ├── CartProvider.tsx
│ │ └── ProductsProvider.tsx
│ ├── images
│ │ ├── item0001.jpg
│ │ ├── item0002.jpg
│ │ └── item0003.jpg
│ ├── index.css
│ ├── main.tsx
│ └── vite-env.d.ts
├── tsconfig.json
├── tsconfig.node.json
└── vite.config.ts
└── lesson17
├── .gitignore
├── data
└── products.json
├── index.html
├── package-lock.json
├── package.json
├── public
└── vite.svg
├── src
├── App.tsx
├── assets
│ └── react.svg
├── components
│ ├── Cart.tsx
│ ├── CartLineItem.tsx
│ ├── Footer.tsx
│ ├── Header.tsx
│ ├── Nav.tsx
│ ├── Product.tsx
│ └── ProductList.tsx
├── context
│ ├── CartProvider.tsx
│ └── ProductsProvider.tsx
├── hooks
│ ├── useCart.tsx
│ └── useProducts.tsx
├── images
│ ├── item0001.jpg
│ ├── item0002.jpg
│ └── item0003.jpg
├── index.css
├── main.tsx
└── vite-env.d.ts
├── tsconfig.json
├── tsconfig.node.json
└── vite.config.ts
/.gitignore:
--------------------------------------------------------------------------------
1 | node_modules
2 | .vscode
3 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # Typescript for Beginners
2 |
3 | ### Full Course - 17 Chapters
4 |
5 | ---
6 |
7 | ### Author Links
8 |
9 | 👋 Hello, I'm Dave Gray.
10 |
11 | ✅ [Check out my YouTube Channel with all of my tutorials](https://www.youtube.com/DaveGrayTeachesCode).
12 |
13 | 🚩 [Subscribe to my channel](https://bit.ly/3nGHmNn)
14 |
15 | ☕ [Buy Me A Coffee](https://buymeacoffee.com/DaveGray)
16 |
17 | 🚀 Follow Me:
18 |
19 | - [Twitter](https://twitter.com/yesdavidgray)
20 | - [LinkedIn](https://www.linkedin.com/in/davidagray/)
21 | - [Blog](https://yesdavidgray.com)
22 | - [Reddit](https://www.reddit.com/user/DaveOnEleven)
23 |
24 | ---
25 |
26 | ### Description
27 |
28 | 📺 [YouTube Playlist](https://www.youtube.com/playlist?list=PL0Zuz27SZ-6NS8GXt5nPrcYpust89zq_b) for this repository.
29 |
30 | 🚀 This repository shares ALL of the resources referenced during the Typescript for Beginners tutorial series.
31 |
32 | - 👉 Chapters 1-10 introduce TypeScript fundamentals.
33 | - 👉 Chapters 12-17 cover Typescript with React.
34 |
35 | ### 📚 Recommended Prerequisites
36 | - 🔗 [JavaScript for Beginners Course](https://youtu.be/EfAl9bwzVZk)
37 | - 🔗 [React for Beginners Course](https://youtu.be/RVFAyFWO4go)
38 |
39 | ---
40 |
41 | ### 🎓 Academic Honesty
42 |
43 | **DO NOT COPY FOR AN ASSIGNMENT** - Avoid plagiarism and adhere to the spirit of this [Academic Honesty Policy](https://www.freecodecamp.org/news/academic-honesty-policy/).
44 |
45 | ---
46 |
47 | ### ⚙ Free Web Dev Tools
48 | - 🔗 [Vite](https://vitejs.dev/)
49 | - 🔗 [Google Chrome Web Browser](https://google.com/chrome/)
50 | - 🔗 [Visual Studio Code (aka VS Code)](https://code.visualstudio.com/)
51 | - 🔗 [Live Server VS Code Extension](https://marketplace.visualstudio.com/items?itemName=ritwickdey.LiveServer)
52 |
53 | ### 📚 References
54 | - 🔗 [Typescript Official Site](https://www.typescriptlang.org/)
55 | - 🔗 [Node.js & npm](https://nodejs.org/)
56 | - 🔗 [Anders Hejlsberg, Creator of Typescript and C# Interview](https://dev.to/destrodevshow/typescript-and-c-both-created-by-the-same-person-named-anders-hejlsberg-42g4)
57 | - 🔗 [Stackoverflow Survey Results](https://survey.stackoverflow.co/2022/#technology-most-popular-technologies)
58 | - - 🔗 [TypeScript + React Cheatsheet](https://github.com/typescript-cheatsheets/react)
59 | - 🔗 [React Official Site](https://reactjs.org/)
60 |
61 | ### 📚 Terminology
62 | - 🔗 [MDN - Static Typing](https://developer.mozilla.org/en-US/docs/Glossary/Static_typing)
63 | - 🔗 [MDN - Dynamic Typing](https://developer.mozilla.org/en-US/docs/Glossary/Dynamic_typing)
64 | - 🔗 [MDN - Type Coercion](https://developer.mozilla.org/en-US/docs/Glossary/Type_coercion)
65 | - 🔗 [TypeScript - Type Assertions](https://www.typescriptlang.org/docs/handbook/2/everyday-types.html#type-assertions)
66 |
67 | ---
68 |
69 | ### 💻 Source Code
70 |
71 | - 🔗 [Chapter 1 - Introduction, Setup & Config](https://github.com/gitdagray/typescript-course/tree/main/lesson01)
72 | - 🔗 [Chapter 2 - Basic Types](https://github.com/gitdagray/typescript-course/tree/main/lesson02)
73 | - 🔗 [Chapter 3 - Arrays, Tuples, Objects & Enums](https://github.com/gitdagray/typescript-course/tree/main/lesson03)
74 | - 🔗 [Chapter 4 - Type Aliases, Literals, Functions & Never Type](https://github.com/gitdagray/typescript-course/tree/main/lesson04)
75 | - 🔗 [Chapter 5 - Type Assertions & Type Casting](https://github.com/gitdagray/typescript-course/tree/main/lesson05)
76 | - 🔗 [Chapter 6 - Classes & Interfaces](https://github.com/gitdagray/typescript-course/tree/main/lesson06)
77 | - 🔗 [Chapter 7 - Index Signatures & keyof Assertions](https://github.com/gitdagray/typescript-course/tree/main/lesson07)
78 | - 🔗 [Chapter 8 - Generics](https://github.com/gitdagray/typescript-course/tree/main/lesson08)
79 | - 🔗 [Chapter 9 - Utility Types](https://github.com/gitdagray/typescript-course/tree/main/lesson09)
80 | - 🔗 Chapter 10 - Vite Introduction (_no source code_)
81 | - 🔗 [Chapter 11 - Beginners Project / Challenges](https://github.com/gitdagray/typescript-course/tree/main/lesson11)
82 | - 🔗 [Chapter 12 - React + Typescript Starter](https://github.com/gitdagray/typescript-course/tree/main/lesson12)
83 | - 🔗 [Chapter 13 - React Hooks + Typescript](https://github.com/gitdagray/typescript-course/tree/main/lesson13)
84 | - 🔗 [Chapter 14 - React useReducer + Typescript](https://github.com/gitdagray/typescript-course/tree/main/lesson14)
85 | - 🔗 [Chapter 15 - React useContext + Typescript](https://github.com/gitdagray/typescript-course/tree/main/lesson15)
86 | - 🔗 [Chapter 16 - React + Typescript Project - Part 1](https://github.com/gitdagray/typescript-course/tree/main/lesson16)
87 |
--------------------------------------------------------------------------------
/lesson01/build/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 | Document
9 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
--------------------------------------------------------------------------------
/lesson01/build/js/main.js:
--------------------------------------------------------------------------------
1 | "use strict";
2 | let username = 'Dave';
3 | console.log(username);
4 | let a = 12;
5 | let b = '6';
6 | let c = 2;
7 | console.log(a / b);
8 | console.log(c * b);
9 |
--------------------------------------------------------------------------------
/lesson01/src/main.ts:
--------------------------------------------------------------------------------
1 | let username = 'Dave'
2 | console.log(username)
3 |
4 | let a: number = 12
5 | let b: string = '6'
6 | let c: number = 2
7 |
8 | console.log(a / b)
9 |
10 | console.log(c * b)
--------------------------------------------------------------------------------
/lesson01/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "compilerOptions": {
3 | /* Visit https://aka.ms/tsconfig to read more about this file */
4 | /* Projects */
5 | // "incremental": true, /* Save .tsbuildinfo files to allow for incremental compilation of projects. */
6 | // "composite": true, /* Enable constraints that allow a TypeScript project to be used with project references. */
7 | // "tsBuildInfoFile": "./.tsbuildinfo", /* Specify the path to .tsbuildinfo incremental compilation file. */
8 | // "disableSourceOfProjectReferenceRedirect": true, /* Disable preferring source files instead of declaration files when referencing composite projects. */
9 | // "disableSolutionSearching": true, /* Opt a project out of multi-project reference checking when editing. */
10 | // "disableReferencedProjectLoad": true, /* Reduce the number of projects loaded automatically by TypeScript. */
11 | /* Language and Environment */
12 | "target": "es2016", /* Set the JavaScript language version for emitted JavaScript and include compatible library declarations. */
13 | // "lib": [], /* Specify a set of bundled library declaration files that describe the target runtime environment. */
14 | // "jsx": "preserve", /* Specify what JSX code is generated. */
15 | // "experimentalDecorators": true, /* Enable experimental support for TC39 stage 2 draft decorators. */
16 | // "emitDecoratorMetadata": true, /* Emit design-type metadata for decorated declarations in source files. */
17 | // "jsxFactory": "", /* Specify the JSX factory function used when targeting React JSX emit, e.g. 'React.createElement' or 'h'. */
18 | // "jsxFragmentFactory": "", /* Specify the JSX Fragment reference used for fragments when targeting React JSX emit e.g. 'React.Fragment' or 'Fragment'. */
19 | // "jsxImportSource": "", /* Specify module specifier used to import the JSX factory functions when using 'jsx: react-jsx*'. */
20 | // "reactNamespace": "", /* Specify the object invoked for 'createElement'. This only applies when targeting 'react' JSX emit. */
21 | // "noLib": true, /* Disable including any library files, including the default lib.d.ts. */
22 | // "useDefineForClassFields": true, /* Emit ECMAScript-standard-compliant class fields. */
23 | // "moduleDetection": "auto", /* Control what method is used to detect module-format JS files. */
24 | /* Modules */
25 | "module": "commonjs", /* Specify what module code is generated. */
26 | "rootDir": "./src", /* Specify the root folder within your source files. */
27 | // "moduleResolution": "node", /* Specify how TypeScript looks up a file from a given module specifier. */
28 | // "baseUrl": "./", /* Specify the base directory to resolve non-relative module names. */
29 | // "paths": {}, /* Specify a set of entries that re-map imports to additional lookup locations. */
30 | // "rootDirs": [], /* Allow multiple folders to be treated as one when resolving modules. */
31 | // "typeRoots": [], /* Specify multiple folders that act like './node_modules/@types'. */
32 | // "types": [], /* Specify type package names to be included without being referenced in a source file. */
33 | // "allowUmdGlobalAccess": true, /* Allow accessing UMD globals from modules. */
34 | // "moduleSuffixes": [], /* List of file name suffixes to search when resolving a module. */
35 | // "resolveJsonModule": true, /* Enable importing .json files. */
36 | // "noResolve": true, /* Disallow 'import's, 'require's or ''s from expanding the number of files TypeScript should add to a project. */
37 | /* JavaScript Support */
38 | // "allowJs": true, /* Allow JavaScript files to be a part of your program. Use the 'checkJS' option to get errors from these files. */
39 | // "checkJs": true, /* Enable error reporting in type-checked JavaScript files. */
40 | // "maxNodeModuleJsDepth": 1, /* Specify the maximum folder depth used for checking JavaScript files from 'node_modules'. Only applicable with 'allowJs'. */
41 | /* Emit */
42 | // "declaration": true, /* Generate .d.ts files from TypeScript and JavaScript files in your project. */
43 | // "declarationMap": true, /* Create sourcemaps for d.ts files. */
44 | // "emitDeclarationOnly": true, /* Only output d.ts files and not JavaScript files. */
45 | // "sourceMap": true, /* Create source map files for emitted JavaScript files. */
46 | // "outFile": "./", /* Specify a file that bundles all outputs into one JavaScript file. If 'declaration' is true, also designates a file that bundles all .d.ts output. */
47 | "outDir": "./build/js", /* Specify an output folder for all emitted files. */
48 | // "removeComments": true, /* Disable emitting comments. */
49 | // "noEmit": true, /* Disable emitting files from a compilation. */
50 | // "importHelpers": true, /* Allow importing helper functions from tslib once per project, instead of including them per-file. */
51 | // "importsNotUsedAsValues": "remove", /* Specify emit/checking behavior for imports that are only used for types. */
52 | // "downlevelIteration": true, /* Emit more compliant, but verbose and less performant JavaScript for iteration. */
53 | // "sourceRoot": "", /* Specify the root path for debuggers to find the reference source code. */
54 | // "mapRoot": "", /* Specify the location where debugger should locate map files instead of generated locations. */
55 | // "inlineSourceMap": true, /* Include sourcemap files inside the emitted JavaScript. */
56 | // "inlineSources": true, /* Include source code in the sourcemaps inside the emitted JavaScript. */
57 | // "emitBOM": true, /* Emit a UTF-8 Byte Order Mark (BOM) in the beginning of output files. */
58 | // "newLine": "crlf", /* Set the newline character for emitting files. */
59 | // "stripInternal": true, /* Disable emitting declarations that have '@internal' in their JSDoc comments. */
60 | // "noEmitHelpers": true, /* Disable generating custom helper functions like '__extends' in compiled output. */
61 | // "noEmitOnError": true, /* Disable emitting files if any type checking errors are reported. */
62 | // "preserveConstEnums": true, /* Disable erasing 'const enum' declarations in generated code. */
63 | // "declarationDir": "./", /* Specify the output directory for generated declaration files. */
64 | // "preserveValueImports": true, /* Preserve unused imported values in the JavaScript output that would otherwise be removed. */
65 | /* Interop Constraints */
66 | // "isolatedModules": true, /* Ensure that each file can be safely transpiled without relying on other imports. */
67 | // "allowSyntheticDefaultImports": true, /* Allow 'import x from y' when a module doesn't have a default export. */
68 | "esModuleInterop": true, /* Emit additional JavaScript to ease support for importing CommonJS modules. This enables 'allowSyntheticDefaultImports' for type compatibility. */
69 | // "preserveSymlinks": true, /* Disable resolving symlinks to their realpath. This correlates to the same flag in node. */
70 | "forceConsistentCasingInFileNames": true, /* Ensure that casing is correct in imports. */
71 | /* Type Checking */
72 | "strict": true, /* Enable all strict type-checking options. */
73 | // "noImplicitAny": true, /* Enable error reporting for expressions and declarations with an implied 'any' type. */
74 | // "strictNullChecks": true, /* When type checking, take into account 'null' and 'undefined'. */
75 | // "strictFunctionTypes": true, /* When assigning functions, check to ensure parameters and the return values are subtype-compatible. */
76 | // "strictBindCallApply": true, /* Check that the arguments for 'bind', 'call', and 'apply' methods match the original function. */
77 | // "strictPropertyInitialization": true, /* Check for class properties that are declared but not set in the constructor. */
78 | // "noImplicitThis": true, /* Enable error reporting when 'this' is given the type 'any'. */
79 | // "useUnknownInCatchVariables": true, /* Default catch clause variables as 'unknown' instead of 'any'. */
80 | // "alwaysStrict": true, /* Ensure 'use strict' is always emitted. */
81 | // "noUnusedLocals": true, /* Enable error reporting when local variables aren't read. */
82 | // "noUnusedParameters": true, /* Raise an error when a function parameter isn't read. */
83 | // "exactOptionalPropertyTypes": true, /* Interpret optional property types as written, rather than adding 'undefined'. */
84 | // "noImplicitReturns": true, /* Enable error reporting for codepaths that do not explicitly return in a function. */
85 | // "noFallthroughCasesInSwitch": true, /* Enable error reporting for fallthrough cases in switch statements. */
86 | // "noUncheckedIndexedAccess": true, /* Add 'undefined' to a type when accessed using an index. */
87 | // "noImplicitOverride": true, /* Ensure overriding members in derived classes are marked with an override modifier. */
88 | // "noPropertyAccessFromIndexSignature": true, /* Enforces using indexed accessors for keys declared using an indexed type. */
89 | // "allowUnusedLabels": true, /* Disable error reporting for unused labels. */
90 | // "allowUnreachableCode": true, /* Disable error reporting for unreachable code. */
91 | /* Completeness */
92 | // "skipDefaultLibCheck": true, /* Skip type checking .d.ts files that are included with TypeScript. */
93 | "skipLibCheck": true /* Skip type checking all .d.ts files. */
94 | },
95 | "include": [
96 | "src"
97 | ]
98 | }
--------------------------------------------------------------------------------
/lesson02/build/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 | Document
9 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
--------------------------------------------------------------------------------
/lesson02/build/js/main.js:
--------------------------------------------------------------------------------
1 | "use strict";
2 | let myName = 'Dave';
3 | let meaningOfLife;
4 | let isLoading;
5 | let album;
6 | myName = 'John';
7 | meaningOfLife = 42;
8 | isLoading = true;
9 | album = 5150;
10 | const sum = (a, b) => {
11 | return a + b;
12 | };
13 | let postId;
14 | let isActive;
15 | let re = /\w+/g;
16 |
--------------------------------------------------------------------------------
/lesson02/src/main.ts:
--------------------------------------------------------------------------------
1 | let myName: string = 'Dave'
2 | let meaningOfLife: number;
3 | let isLoading: boolean;
4 | let album: any;
5 |
6 | myName = 'John'
7 | meaningOfLife = 42
8 | isLoading = true
9 | album = 5150
10 |
11 | const sum = (a: number, b: string) => {
12 | return a + b
13 | }
14 |
15 | let postId: string | number
16 | let isActive: number | boolean
17 |
18 | let re: RegExp = /\w+/g
--------------------------------------------------------------------------------
/lesson02/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "compilerOptions": {
3 | /* Visit https://aka.ms/tsconfig to read more about this file */
4 | /* Projects */
5 | // "incremental": true, /* Save .tsbuildinfo files to allow for incremental compilation of projects. */
6 | // "composite": true, /* Enable constraints that allow a TypeScript project to be used with project references. */
7 | // "tsBuildInfoFile": "./.tsbuildinfo", /* Specify the path to .tsbuildinfo incremental compilation file. */
8 | // "disableSourceOfProjectReferenceRedirect": true, /* Disable preferring source files instead of declaration files when referencing composite projects. */
9 | // "disableSolutionSearching": true, /* Opt a project out of multi-project reference checking when editing. */
10 | // "disableReferencedProjectLoad": true, /* Reduce the number of projects loaded automatically by TypeScript. */
11 | /* Language and Environment */
12 | "target": "es2016", /* Set the JavaScript language version for emitted JavaScript and include compatible library declarations. */
13 | // "lib": [], /* Specify a set of bundled library declaration files that describe the target runtime environment. */
14 | // "jsx": "preserve", /* Specify what JSX code is generated. */
15 | // "experimentalDecorators": true, /* Enable experimental support for TC39 stage 2 draft decorators. */
16 | // "emitDecoratorMetadata": true, /* Emit design-type metadata for decorated declarations in source files. */
17 | // "jsxFactory": "", /* Specify the JSX factory function used when targeting React JSX emit, e.g. 'React.createElement' or 'h'. */
18 | // "jsxFragmentFactory": "", /* Specify the JSX Fragment reference used for fragments when targeting React JSX emit e.g. 'React.Fragment' or 'Fragment'. */
19 | // "jsxImportSource": "", /* Specify module specifier used to import the JSX factory functions when using 'jsx: react-jsx*'. */
20 | // "reactNamespace": "", /* Specify the object invoked for 'createElement'. This only applies when targeting 'react' JSX emit. */
21 | // "noLib": true, /* Disable including any library files, including the default lib.d.ts. */
22 | // "useDefineForClassFields": true, /* Emit ECMAScript-standard-compliant class fields. */
23 | // "moduleDetection": "auto", /* Control what method is used to detect module-format JS files. */
24 | /* Modules */
25 | "module": "commonjs", /* Specify what module code is generated. */
26 | "rootDir": "./src", /* Specify the root folder within your source files. */
27 | // "moduleResolution": "node", /* Specify how TypeScript looks up a file from a given module specifier. */
28 | // "baseUrl": "./", /* Specify the base directory to resolve non-relative module names. */
29 | // "paths": {}, /* Specify a set of entries that re-map imports to additional lookup locations. */
30 | // "rootDirs": [], /* Allow multiple folders to be treated as one when resolving modules. */
31 | // "typeRoots": [], /* Specify multiple folders that act like './node_modules/@types'. */
32 | // "types": [], /* Specify type package names to be included without being referenced in a source file. */
33 | // "allowUmdGlobalAccess": true, /* Allow accessing UMD globals from modules. */
34 | // "moduleSuffixes": [], /* List of file name suffixes to search when resolving a module. */
35 | // "resolveJsonModule": true, /* Enable importing .json files. */
36 | // "noResolve": true, /* Disallow 'import's, 'require's or ''s from expanding the number of files TypeScript should add to a project. */
37 | /* JavaScript Support */
38 | // "allowJs": true, /* Allow JavaScript files to be a part of your program. Use the 'checkJS' option to get errors from these files. */
39 | // "checkJs": true, /* Enable error reporting in type-checked JavaScript files. */
40 | // "maxNodeModuleJsDepth": 1, /* Specify the maximum folder depth used for checking JavaScript files from 'node_modules'. Only applicable with 'allowJs'. */
41 | /* Emit */
42 | // "declaration": true, /* Generate .d.ts files from TypeScript and JavaScript files in your project. */
43 | // "declarationMap": true, /* Create sourcemaps for d.ts files. */
44 | // "emitDeclarationOnly": true, /* Only output d.ts files and not JavaScript files. */
45 | // "sourceMap": true, /* Create source map files for emitted JavaScript files. */
46 | // "outFile": "./", /* Specify a file that bundles all outputs into one JavaScript file. If 'declaration' is true, also designates a file that bundles all .d.ts output. */
47 | "outDir": "./build/js", /* Specify an output folder for all emitted files. */
48 | // "removeComments": true, /* Disable emitting comments. */
49 | // "noEmit": true, /* Disable emitting files from a compilation. */
50 | // "importHelpers": true, /* Allow importing helper functions from tslib once per project, instead of including them per-file. */
51 | // "importsNotUsedAsValues": "remove", /* Specify emit/checking behavior for imports that are only used for types. */
52 | // "downlevelIteration": true, /* Emit more compliant, but verbose and less performant JavaScript for iteration. */
53 | // "sourceRoot": "", /* Specify the root path for debuggers to find the reference source code. */
54 | // "mapRoot": "", /* Specify the location where debugger should locate map files instead of generated locations. */
55 | // "inlineSourceMap": true, /* Include sourcemap files inside the emitted JavaScript. */
56 | // "inlineSources": true, /* Include source code in the sourcemaps inside the emitted JavaScript. */
57 | // "emitBOM": true, /* Emit a UTF-8 Byte Order Mark (BOM) in the beginning of output files. */
58 | // "newLine": "crlf", /* Set the newline character for emitting files. */
59 | // "stripInternal": true, /* Disable emitting declarations that have '@internal' in their JSDoc comments. */
60 | // "noEmitHelpers": true, /* Disable generating custom helper functions like '__extends' in compiled output. */
61 | // "noEmitOnError": true, /* Disable emitting files if any type checking errors are reported. */
62 | // "preserveConstEnums": true, /* Disable erasing 'const enum' declarations in generated code. */
63 | // "declarationDir": "./", /* Specify the output directory for generated declaration files. */
64 | // "preserveValueImports": true, /* Preserve unused imported values in the JavaScript output that would otherwise be removed. */
65 | /* Interop Constraints */
66 | // "isolatedModules": true, /* Ensure that each file can be safely transpiled without relying on other imports. */
67 | // "allowSyntheticDefaultImports": true, /* Allow 'import x from y' when a module doesn't have a default export. */
68 | "esModuleInterop": true, /* Emit additional JavaScript to ease support for importing CommonJS modules. This enables 'allowSyntheticDefaultImports' for type compatibility. */
69 | // "preserveSymlinks": true, /* Disable resolving symlinks to their realpath. This correlates to the same flag in node. */
70 | "forceConsistentCasingInFileNames": true, /* Ensure that casing is correct in imports. */
71 | /* Type Checking */
72 | "strict": true, /* Enable all strict type-checking options. */
73 | // "noImplicitAny": true, /* Enable error reporting for expressions and declarations with an implied 'any' type. */
74 | // "strictNullChecks": true, /* When type checking, take into account 'null' and 'undefined'. */
75 | // "strictFunctionTypes": true, /* When assigning functions, check to ensure parameters and the return values are subtype-compatible. */
76 | // "strictBindCallApply": true, /* Check that the arguments for 'bind', 'call', and 'apply' methods match the original function. */
77 | // "strictPropertyInitialization": true, /* Check for class properties that are declared but not set in the constructor. */
78 | // "noImplicitThis": true, /* Enable error reporting when 'this' is given the type 'any'. */
79 | // "useUnknownInCatchVariables": true, /* Default catch clause variables as 'unknown' instead of 'any'. */
80 | // "alwaysStrict": true, /* Ensure 'use strict' is always emitted. */
81 | // "noUnusedLocals": true, /* Enable error reporting when local variables aren't read. */
82 | // "noUnusedParameters": true, /* Raise an error when a function parameter isn't read. */
83 | // "exactOptionalPropertyTypes": true, /* Interpret optional property types as written, rather than adding 'undefined'. */
84 | // "noImplicitReturns": true, /* Enable error reporting for codepaths that do not explicitly return in a function. */
85 | // "noFallthroughCasesInSwitch": true, /* Enable error reporting for fallthrough cases in switch statements. */
86 | // "noUncheckedIndexedAccess": true, /* Add 'undefined' to a type when accessed using an index. */
87 | // "noImplicitOverride": true, /* Ensure overriding members in derived classes are marked with an override modifier. */
88 | // "noPropertyAccessFromIndexSignature": true, /* Enforces using indexed accessors for keys declared using an indexed type. */
89 | // "allowUnusedLabels": true, /* Disable error reporting for unused labels. */
90 | // "allowUnreachableCode": true, /* Disable error reporting for unreachable code. */
91 | /* Completeness */
92 | // "skipDefaultLibCheck": true, /* Skip type checking .d.ts files that are included with TypeScript. */
93 | "skipLibCheck": true /* Skip type checking all .d.ts files. */
94 | },
95 | "include": [
96 | "src"
97 | ]
98 | }
--------------------------------------------------------------------------------
/lesson03/build/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 | Document
9 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
--------------------------------------------------------------------------------
/lesson03/build/js/main.js:
--------------------------------------------------------------------------------
1 | "use strict";
2 | let stringArr = ['one', 'hey', 'Dave'];
3 | let guitars = ['Strat', 'Les Paul', 5150];
4 | let mixedData = ['EVH', 1984, true];
5 | stringArr[0] = 'John';
6 | stringArr.push('hey');
7 | guitars[0] = 1984;
8 | guitars.unshift('Jim');
9 | let test = [];
10 | let bands = [];
11 | bands.push('Van Halen');
12 | // Tuple
13 | let myTuple = ['Dave', 42, true];
14 | let mixed = ['John', 1, false];
15 | myTuple[1] = 42;
16 | // Objects
17 | let myObj;
18 | myObj = [];
19 | console.log(typeof myObj);
20 | myObj = bands;
21 | myObj = {};
22 | const exampleObj = {
23 | prop1: 'Dave',
24 | prop2: true,
25 | };
26 | exampleObj.prop1 = 'John';
27 | let evh = {
28 | name: 'Eddie',
29 | active: false,
30 | albums: [1984, 5150, 'OU812']
31 | };
32 | let jp = {
33 | active: true,
34 | albums: ['I', 'II', 'IV']
35 | };
36 | const greetGuitarist = (guitarist) => {
37 | if (guitarist.name) {
38 | return `Hello ${guitarist.name.toUpperCase()}!`;
39 | }
40 | return 'Hello!';
41 | };
42 | console.log(greetGuitarist(jp));
43 | // Enums
44 | // "Unlike most TypeScript features, Enums are not a type-level addition to JavaScript but something added to the language and runtime."
45 | var Grade;
46 | (function (Grade) {
47 | Grade[Grade["U"] = 1] = "U";
48 | Grade[Grade["D"] = 2] = "D";
49 | Grade[Grade["C"] = 3] = "C";
50 | Grade[Grade["B"] = 4] = "B";
51 | Grade[Grade["A"] = 5] = "A";
52 | })(Grade || (Grade = {}));
53 | console.log(Grade.U);
54 |
--------------------------------------------------------------------------------
/lesson03/src/main.ts:
--------------------------------------------------------------------------------
1 | let stringArr = ["one", "hey", "Dave"];
2 |
3 | let guitars = ["Strat", "Les Paul", 5150];
4 |
5 | let mixedData = ["EVH", 1984, true];
6 |
7 | stringArr[0] = "John";
8 | stringArr.push("hey");
9 |
10 | guitars[0] = 1984;
11 | guitars.unshift("Jim");
12 |
13 | let test = [];
14 | let bands: string[] = [];
15 | bands.push("Van Halen");
16 |
17 | // Tuple
18 | let myTuple: [string, number, boolean] = ["Dave", 42, true];
19 |
20 | let mixed = ["John", 1, false];
21 |
22 | myTuple[1] = 42;
23 |
24 | // Objects
25 | let myObj: object;
26 | myObj = [];
27 | console.log(typeof myObj);
28 | myObj = bands;
29 | myObj = {};
30 |
31 | const exampleObj = {
32 | prop1: "Dave",
33 | prop2: true,
34 | };
35 |
36 | exampleObj.prop1 = "John";
37 |
38 | interface Guitarist {
39 | name?: string;
40 | active: boolean;
41 | albums: (string | number)[];
42 | }
43 |
44 | let evh: Guitarist = {
45 | name: "Eddie",
46 | active: false,
47 | albums: [1984, 5150, "OU812"],
48 | };
49 |
50 | let jp: Guitarist = {
51 | active: true,
52 | albums: ["I", "II", "IV"],
53 | };
54 |
55 | const greetGuitarist = (guitarist: Guitarist) => {
56 | if (guitarist.name) {
57 | return `Hello ${guitarist.name.toUpperCase()}!`;
58 | }
59 | return "Hello!";
60 | };
61 |
62 | console.log(greetGuitarist(jp));
63 |
64 | // Enums
65 | // "Unlike most TypeScript features, Enums are not a type-level addition to JavaScript but something added to the language and runtime."
66 |
67 | enum Grade {
68 | U = 1,
69 | D,
70 | C,
71 | B,
72 | A,
73 | }
74 |
75 | console.log(Grade.U);
76 |
--------------------------------------------------------------------------------
/lesson03/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "compilerOptions": {
3 | /* Visit https://aka.ms/tsconfig to read more about this file */
4 | /* Projects */
5 | // "incremental": true, /* Save .tsbuildinfo files to allow for incremental compilation of projects. */
6 | // "composite": true, /* Enable constraints that allow a TypeScript project to be used with project references. */
7 | // "tsBuildInfoFile": "./.tsbuildinfo", /* Specify the path to .tsbuildinfo incremental compilation file. */
8 | // "disableSourceOfProjectReferenceRedirect": true, /* Disable preferring source files instead of declaration files when referencing composite projects. */
9 | // "disableSolutionSearching": true, /* Opt a project out of multi-project reference checking when editing. */
10 | // "disableReferencedProjectLoad": true, /* Reduce the number of projects loaded automatically by TypeScript. */
11 | /* Language and Environment */
12 | "target": "es2016", /* Set the JavaScript language version for emitted JavaScript and include compatible library declarations. */
13 | // "lib": [], /* Specify a set of bundled library declaration files that describe the target runtime environment. */
14 | // "jsx": "preserve", /* Specify what JSX code is generated. */
15 | // "experimentalDecorators": true, /* Enable experimental support for TC39 stage 2 draft decorators. */
16 | // "emitDecoratorMetadata": true, /* Emit design-type metadata for decorated declarations in source files. */
17 | // "jsxFactory": "", /* Specify the JSX factory function used when targeting React JSX emit, e.g. 'React.createElement' or 'h'. */
18 | // "jsxFragmentFactory": "", /* Specify the JSX Fragment reference used for fragments when targeting React JSX emit e.g. 'React.Fragment' or 'Fragment'. */
19 | // "jsxImportSource": "", /* Specify module specifier used to import the JSX factory functions when using 'jsx: react-jsx*'. */
20 | // "reactNamespace": "", /* Specify the object invoked for 'createElement'. This only applies when targeting 'react' JSX emit. */
21 | // "noLib": true, /* Disable including any library files, including the default lib.d.ts. */
22 | // "useDefineForClassFields": true, /* Emit ECMAScript-standard-compliant class fields. */
23 | // "moduleDetection": "auto", /* Control what method is used to detect module-format JS files. */
24 | /* Modules */
25 | "module": "commonjs", /* Specify what module code is generated. */
26 | "rootDir": "./src", /* Specify the root folder within your source files. */
27 | // "moduleResolution": "node", /* Specify how TypeScript looks up a file from a given module specifier. */
28 | // "baseUrl": "./", /* Specify the base directory to resolve non-relative module names. */
29 | // "paths": {}, /* Specify a set of entries that re-map imports to additional lookup locations. */
30 | // "rootDirs": [], /* Allow multiple folders to be treated as one when resolving modules. */
31 | // "typeRoots": [], /* Specify multiple folders that act like './node_modules/@types'. */
32 | // "types": [], /* Specify type package names to be included without being referenced in a source file. */
33 | // "allowUmdGlobalAccess": true, /* Allow accessing UMD globals from modules. */
34 | // "moduleSuffixes": [], /* List of file name suffixes to search when resolving a module. */
35 | // "resolveJsonModule": true, /* Enable importing .json files. */
36 | // "noResolve": true, /* Disallow 'import's, 'require's or ''s from expanding the number of files TypeScript should add to a project. */
37 | /* JavaScript Support */
38 | // "allowJs": true, /* Allow JavaScript files to be a part of your program. Use the 'checkJS' option to get errors from these files. */
39 | // "checkJs": true, /* Enable error reporting in type-checked JavaScript files. */
40 | // "maxNodeModuleJsDepth": 1, /* Specify the maximum folder depth used for checking JavaScript files from 'node_modules'. Only applicable with 'allowJs'. */
41 | /* Emit */
42 | // "declaration": true, /* Generate .d.ts files from TypeScript and JavaScript files in your project. */
43 | // "declarationMap": true, /* Create sourcemaps for d.ts files. */
44 | // "emitDeclarationOnly": true, /* Only output d.ts files and not JavaScript files. */
45 | // "sourceMap": true, /* Create source map files for emitted JavaScript files. */
46 | // "outFile": "./", /* Specify a file that bundles all outputs into one JavaScript file. If 'declaration' is true, also designates a file that bundles all .d.ts output. */
47 | "outDir": "./build/js", /* Specify an output folder for all emitted files. */
48 | // "removeComments": true, /* Disable emitting comments. */
49 | // "noEmit": true, /* Disable emitting files from a compilation. */
50 | // "importHelpers": true, /* Allow importing helper functions from tslib once per project, instead of including them per-file. */
51 | // "importsNotUsedAsValues": "remove", /* Specify emit/checking behavior for imports that are only used for types. */
52 | // "downlevelIteration": true, /* Emit more compliant, but verbose and less performant JavaScript for iteration. */
53 | // "sourceRoot": "", /* Specify the root path for debuggers to find the reference source code. */
54 | // "mapRoot": "", /* Specify the location where debugger should locate map files instead of generated locations. */
55 | // "inlineSourceMap": true, /* Include sourcemap files inside the emitted JavaScript. */
56 | // "inlineSources": true, /* Include source code in the sourcemaps inside the emitted JavaScript. */
57 | // "emitBOM": true, /* Emit a UTF-8 Byte Order Mark (BOM) in the beginning of output files. */
58 | // "newLine": "crlf", /* Set the newline character for emitting files. */
59 | // "stripInternal": true, /* Disable emitting declarations that have '@internal' in their JSDoc comments. */
60 | // "noEmitHelpers": true, /* Disable generating custom helper functions like '__extends' in compiled output. */
61 | // "noEmitOnError": true, /* Disable emitting files if any type checking errors are reported. */
62 | // "preserveConstEnums": true, /* Disable erasing 'const enum' declarations in generated code. */
63 | // "declarationDir": "./", /* Specify the output directory for generated declaration files. */
64 | // "preserveValueImports": true, /* Preserve unused imported values in the JavaScript output that would otherwise be removed. */
65 | /* Interop Constraints */
66 | // "isolatedModules": true, /* Ensure that each file can be safely transpiled without relying on other imports. */
67 | // "allowSyntheticDefaultImports": true, /* Allow 'import x from y' when a module doesn't have a default export. */
68 | "esModuleInterop": true, /* Emit additional JavaScript to ease support for importing CommonJS modules. This enables 'allowSyntheticDefaultImports' for type compatibility. */
69 | // "preserveSymlinks": true, /* Disable resolving symlinks to their realpath. This correlates to the same flag in node. */
70 | "forceConsistentCasingInFileNames": true, /* Ensure that casing is correct in imports. */
71 | /* Type Checking */
72 | "strict": true, /* Enable all strict type-checking options. */
73 | // "noImplicitAny": true, /* Enable error reporting for expressions and declarations with an implied 'any' type. */
74 | // "strictNullChecks": true, /* When type checking, take into account 'null' and 'undefined'. */
75 | // "strictFunctionTypes": true, /* When assigning functions, check to ensure parameters and the return values are subtype-compatible. */
76 | // "strictBindCallApply": true, /* Check that the arguments for 'bind', 'call', and 'apply' methods match the original function. */
77 | // "strictPropertyInitialization": true, /* Check for class properties that are declared but not set in the constructor. */
78 | // "noImplicitThis": true, /* Enable error reporting when 'this' is given the type 'any'. */
79 | // "useUnknownInCatchVariables": true, /* Default catch clause variables as 'unknown' instead of 'any'. */
80 | // "alwaysStrict": true, /* Ensure 'use strict' is always emitted. */
81 | // "noUnusedLocals": true, /* Enable error reporting when local variables aren't read. */
82 | // "noUnusedParameters": true, /* Raise an error when a function parameter isn't read. */
83 | // "exactOptionalPropertyTypes": true, /* Interpret optional property types as written, rather than adding 'undefined'. */
84 | // "noImplicitReturns": true, /* Enable error reporting for codepaths that do not explicitly return in a function. */
85 | // "noFallthroughCasesInSwitch": true, /* Enable error reporting for fallthrough cases in switch statements. */
86 | // "noUncheckedIndexedAccess": true, /* Add 'undefined' to a type when accessed using an index. */
87 | // "noImplicitOverride": true, /* Ensure overriding members in derived classes are marked with an override modifier. */
88 | // "noPropertyAccessFromIndexSignature": true, /* Enforces using indexed accessors for keys declared using an indexed type. */
89 | // "allowUnusedLabels": true, /* Disable error reporting for unused labels. */
90 | // "allowUnreachableCode": true, /* Disable error reporting for unreachable code. */
91 | /* Completeness */
92 | // "skipDefaultLibCheck": true, /* Skip type checking .d.ts files that are included with TypeScript. */
93 | "skipLibCheck": true /* Skip type checking all .d.ts files. */
94 | },
95 | "include": [
96 | "src"
97 | ]
98 | }
--------------------------------------------------------------------------------
/lesson04/build/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 | Document
9 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
--------------------------------------------------------------------------------
/lesson04/build/js/main.js:
--------------------------------------------------------------------------------
1 | "use strict";
2 | // Literal types
3 | let myName;
4 | let userName;
5 | userName = 'Amy';
6 | // functions
7 | const add = (a, b) => {
8 | return a + b;
9 | };
10 | const logMsg = (message) => {
11 | console.log(message);
12 | };
13 | logMsg('Hello!');
14 | logMsg(add(2, 3));
15 | let subtract = function (c, d) {
16 | return c - d;
17 | };
18 | // interface mathFunction {
19 | // (a: number, b: number): number
20 | // }
21 | let multiply = function (c, d) {
22 | return c * d;
23 | };
24 | logMsg(multiply(2, 2));
25 | // optional parameters
26 | const addAll = (a, b, c) => {
27 | if (typeof c !== 'undefined') {
28 | return a + b + c;
29 | }
30 | return a + b;
31 | };
32 | // default param value
33 | const sumAll = (a = 10, b, c = 2) => {
34 | return a + b + c;
35 | };
36 | logMsg(addAll(2, 3, 2));
37 | logMsg(addAll(2, 3));
38 | logMsg(sumAll(2, 3));
39 | logMsg(sumAll(undefined, 3));
40 | // Rest Parameters
41 | const total = (a, ...nums) => {
42 | return a + nums.reduce((prev, curr) => prev + curr);
43 | };
44 | logMsg(total(10, 2, 3));
45 | const createError = (errMsg) => {
46 | throw new Error(errMsg);
47 | };
48 | const infinite = () => {
49 | let i = 1;
50 | while (true) {
51 | i++;
52 | if (i > 100)
53 | break;
54 | }
55 | };
56 | // custom type guard
57 | const isNumber = (value) => {
58 | return typeof value === 'number'
59 | ? true : false;
60 | };
61 | // use of the never type
62 | const numberOrString = (value) => {
63 | if (typeof value === 'string')
64 | return 'string';
65 | if (isNumber(value))
66 | return 'number';
67 | return createError('This should never happen!');
68 | };
69 |
--------------------------------------------------------------------------------
/lesson04/src/main.ts:
--------------------------------------------------------------------------------
1 | // Type Aliases
2 | type stringOrNumber = string | number
3 |
4 | type stringOrNumberArray = (string | number)[]
5 |
6 | type Guitarist = {
7 | name?: string,
8 | active: boolean,
9 | albums: stringOrNumberArray
10 | }
11 |
12 | type UserId = stringOrNumber
13 |
14 | // Literal types
15 | let myName: 'Dave'
16 |
17 | let userName: 'Dave' | 'John' | 'Amy'
18 | userName = 'Amy'
19 |
20 | // functions
21 | const add = (a: number, b: number): number => {
22 | return a + b
23 | }
24 |
25 | const logMsg = (message: any): void => {
26 | console.log(message)
27 | }
28 |
29 | logMsg('Hello!')
30 | logMsg(add(2, 3))
31 |
32 | let subtract = function (c: number, d: number): number {
33 | return c - d
34 | }
35 |
36 | type mathFunction = (a: number, b: number) => number
37 | // interface mathFunction {
38 | // (a: number, b: number): number
39 | // }
40 |
41 | let multiply: mathFunction = function (c, d) {
42 | return c * d
43 | }
44 |
45 | logMsg(multiply(2, 2))
46 |
47 | // optional parameters
48 | const addAll = (a: number, b: number, c?: number): number => {
49 | if (typeof c !== 'undefined') {
50 | return a + b + c
51 | }
52 | return a + b
53 | }
54 |
55 | // default param value
56 | const sumAll = (a: number = 10, b: number, c: number = 2): number => {
57 | return a + b + c
58 | }
59 |
60 | logMsg(addAll(2, 3, 2))
61 | logMsg(addAll(2, 3))
62 | logMsg(sumAll(2, 3))
63 | logMsg(sumAll(undefined, 3))
64 |
65 | // Rest Parameters
66 | const total = (a: number, ...nums: number[]): number => {
67 | return a + nums.reduce((prev, curr) => prev + curr)
68 | }
69 |
70 | logMsg(total(10, 2, 3))
71 |
72 | const createError = (errMsg: string): never => {
73 | throw new Error(errMsg)
74 | }
75 |
76 | const infinite = () => {
77 | let i: number = 1
78 | while (true) {
79 | i++
80 | if (i > 100) break
81 | }
82 | }
83 |
84 | // custom type guard
85 | const isNumber = (value: any): boolean => {
86 | return typeof value === 'number'
87 | ? true : false
88 | }
89 |
90 | // use of the never type
91 | const numberOrString = (value: number | string): string => {
92 | if (typeof value === 'string') return 'string'
93 | if (isNumber(value)) return 'number'
94 | return createError('This should never happen!')
95 | }
--------------------------------------------------------------------------------
/lesson04/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "compilerOptions": {
3 | /* Visit https://aka.ms/tsconfig to read more about this file */
4 | /* Projects */
5 | // "incremental": true, /* Save .tsbuildinfo files to allow for incremental compilation of projects. */
6 | // "composite": true, /* Enable constraints that allow a TypeScript project to be used with project references. */
7 | // "tsBuildInfoFile": "./.tsbuildinfo", /* Specify the path to .tsbuildinfo incremental compilation file. */
8 | // "disableSourceOfProjectReferenceRedirect": true, /* Disable preferring source files instead of declaration files when referencing composite projects. */
9 | // "disableSolutionSearching": true, /* Opt a project out of multi-project reference checking when editing. */
10 | // "disableReferencedProjectLoad": true, /* Reduce the number of projects loaded automatically by TypeScript. */
11 | /* Language and Environment */
12 | "target": "es2016", /* Set the JavaScript language version for emitted JavaScript and include compatible library declarations. */
13 | // "lib": [], /* Specify a set of bundled library declaration files that describe the target runtime environment. */
14 | // "jsx": "preserve", /* Specify what JSX code is generated. */
15 | // "experimentalDecorators": true, /* Enable experimental support for TC39 stage 2 draft decorators. */
16 | // "emitDecoratorMetadata": true, /* Emit design-type metadata for decorated declarations in source files. */
17 | // "jsxFactory": "", /* Specify the JSX factory function used when targeting React JSX emit, e.g. 'React.createElement' or 'h'. */
18 | // "jsxFragmentFactory": "", /* Specify the JSX Fragment reference used for fragments when targeting React JSX emit e.g. 'React.Fragment' or 'Fragment'. */
19 | // "jsxImportSource": "", /* Specify module specifier used to import the JSX factory functions when using 'jsx: react-jsx*'. */
20 | // "reactNamespace": "", /* Specify the object invoked for 'createElement'. This only applies when targeting 'react' JSX emit. */
21 | // "noLib": true, /* Disable including any library files, including the default lib.d.ts. */
22 | // "useDefineForClassFields": true, /* Emit ECMAScript-standard-compliant class fields. */
23 | // "moduleDetection": "auto", /* Control what method is used to detect module-format JS files. */
24 | /* Modules */
25 | "module": "commonjs", /* Specify what module code is generated. */
26 | "rootDir": "./src", /* Specify the root folder within your source files. */
27 | // "moduleResolution": "node", /* Specify how TypeScript looks up a file from a given module specifier. */
28 | // "baseUrl": "./", /* Specify the base directory to resolve non-relative module names. */
29 | // "paths": {}, /* Specify a set of entries that re-map imports to additional lookup locations. */
30 | // "rootDirs": [], /* Allow multiple folders to be treated as one when resolving modules. */
31 | // "typeRoots": [], /* Specify multiple folders that act like './node_modules/@types'. */
32 | // "types": [], /* Specify type package names to be included without being referenced in a source file. */
33 | // "allowUmdGlobalAccess": true, /* Allow accessing UMD globals from modules. */
34 | // "moduleSuffixes": [], /* List of file name suffixes to search when resolving a module. */
35 | // "resolveJsonModule": true, /* Enable importing .json files. */
36 | // "noResolve": true, /* Disallow 'import's, 'require's or ''s from expanding the number of files TypeScript should add to a project. */
37 | /* JavaScript Support */
38 | // "allowJs": true, /* Allow JavaScript files to be a part of your program. Use the 'checkJS' option to get errors from these files. */
39 | // "checkJs": true, /* Enable error reporting in type-checked JavaScript files. */
40 | // "maxNodeModuleJsDepth": 1, /* Specify the maximum folder depth used for checking JavaScript files from 'node_modules'. Only applicable with 'allowJs'. */
41 | /* Emit */
42 | // "declaration": true, /* Generate .d.ts files from TypeScript and JavaScript files in your project. */
43 | // "declarationMap": true, /* Create sourcemaps for d.ts files. */
44 | // "emitDeclarationOnly": true, /* Only output d.ts files and not JavaScript files. */
45 | // "sourceMap": true, /* Create source map files for emitted JavaScript files. */
46 | // "outFile": "./", /* Specify a file that bundles all outputs into one JavaScript file. If 'declaration' is true, also designates a file that bundles all .d.ts output. */
47 | "outDir": "./build/js", /* Specify an output folder for all emitted files. */
48 | // "removeComments": true, /* Disable emitting comments. */
49 | // "noEmit": true, /* Disable emitting files from a compilation. */
50 | // "importHelpers": true, /* Allow importing helper functions from tslib once per project, instead of including them per-file. */
51 | // "importsNotUsedAsValues": "remove", /* Specify emit/checking behavior for imports that are only used for types. */
52 | // "downlevelIteration": true, /* Emit more compliant, but verbose and less performant JavaScript for iteration. */
53 | // "sourceRoot": "", /* Specify the root path for debuggers to find the reference source code. */
54 | // "mapRoot": "", /* Specify the location where debugger should locate map files instead of generated locations. */
55 | // "inlineSourceMap": true, /* Include sourcemap files inside the emitted JavaScript. */
56 | // "inlineSources": true, /* Include source code in the sourcemaps inside the emitted JavaScript. */
57 | // "emitBOM": true, /* Emit a UTF-8 Byte Order Mark (BOM) in the beginning of output files. */
58 | // "newLine": "crlf", /* Set the newline character for emitting files. */
59 | // "stripInternal": true, /* Disable emitting declarations that have '@internal' in their JSDoc comments. */
60 | // "noEmitHelpers": true, /* Disable generating custom helper functions like '__extends' in compiled output. */
61 | // "noEmitOnError": true, /* Disable emitting files if any type checking errors are reported. */
62 | // "preserveConstEnums": true, /* Disable erasing 'const enum' declarations in generated code. */
63 | // "declarationDir": "./", /* Specify the output directory for generated declaration files. */
64 | // "preserveValueImports": true, /* Preserve unused imported values in the JavaScript output that would otherwise be removed. */
65 | /* Interop Constraints */
66 | // "isolatedModules": true, /* Ensure that each file can be safely transpiled without relying on other imports. */
67 | // "allowSyntheticDefaultImports": true, /* Allow 'import x from y' when a module doesn't have a default export. */
68 | "esModuleInterop": true, /* Emit additional JavaScript to ease support for importing CommonJS modules. This enables 'allowSyntheticDefaultImports' for type compatibility. */
69 | // "preserveSymlinks": true, /* Disable resolving symlinks to their realpath. This correlates to the same flag in node. */
70 | "forceConsistentCasingInFileNames": true, /* Ensure that casing is correct in imports. */
71 | /* Type Checking */
72 | "strict": true, /* Enable all strict type-checking options. */
73 | // "noImplicitAny": true, /* Enable error reporting for expressions and declarations with an implied 'any' type. */
74 | // "strictNullChecks": true, /* When type checking, take into account 'null' and 'undefined'. */
75 | // "strictFunctionTypes": true, /* When assigning functions, check to ensure parameters and the return values are subtype-compatible. */
76 | // "strictBindCallApply": true, /* Check that the arguments for 'bind', 'call', and 'apply' methods match the original function. */
77 | // "strictPropertyInitialization": true, /* Check for class properties that are declared but not set in the constructor. */
78 | // "noImplicitThis": true, /* Enable error reporting when 'this' is given the type 'any'. */
79 | // "useUnknownInCatchVariables": true, /* Default catch clause variables as 'unknown' instead of 'any'. */
80 | // "alwaysStrict": true, /* Ensure 'use strict' is always emitted. */
81 | // "noUnusedLocals": true, /* Enable error reporting when local variables aren't read. */
82 | // "noUnusedParameters": true, /* Raise an error when a function parameter isn't read. */
83 | // "exactOptionalPropertyTypes": true, /* Interpret optional property types as written, rather than adding 'undefined'. */
84 | // "noImplicitReturns": true, /* Enable error reporting for codepaths that do not explicitly return in a function. */
85 | // "noFallthroughCasesInSwitch": true, /* Enable error reporting for fallthrough cases in switch statements. */
86 | // "noUncheckedIndexedAccess": true, /* Add 'undefined' to a type when accessed using an index. */
87 | // "noImplicitOverride": true, /* Ensure overriding members in derived classes are marked with an override modifier. */
88 | // "noPropertyAccessFromIndexSignature": true, /* Enforces using indexed accessors for keys declared using an indexed type. */
89 | // "allowUnusedLabels": true, /* Disable error reporting for unused labels. */
90 | // "allowUnreachableCode": true, /* Disable error reporting for unreachable code. */
91 | /* Completeness */
92 | // "skipDefaultLibCheck": true, /* Skip type checking .d.ts files that are included with TypeScript. */
93 | "skipLibCheck": true /* Skip type checking all .d.ts files. */
94 | },
95 | "include": [
96 | "src"
97 | ]
98 | }
--------------------------------------------------------------------------------
/lesson05/build/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 | Copyright
9 |
20 |
21 |
22 |
23 |
24 | Copyright ©
25 |
26 |
27 |
--------------------------------------------------------------------------------
/lesson05/build/js/copyright.js:
--------------------------------------------------------------------------------
1 | "use strict";
2 | // Original JS code
3 | // const year = document.getElementById("year")
4 | // const thisYear = new Date().getFullYear()
5 | // year.setAttribute("datetime", thisYear)
6 | // year.textContent = thisYear
7 | // 1st variation:
8 | // let year: HTMLElement | null
9 | // year = document.getElementById("year")
10 | // let thisYear: string
11 | // thisYear = new Date().getFullYear().toString()
12 | // if (year) {
13 | // year.setAttribute("datetime", thisYear)
14 | // year.textContent = thisYear
15 | // }
16 | // 2nd variation:
17 | const year = document.getElementById("year");
18 | const thisYear = new Date().getFullYear().toString();
19 | year.setAttribute("datetime", thisYear);
20 | year.textContent = thisYear;
21 |
--------------------------------------------------------------------------------
/lesson05/build/js/main.js:
--------------------------------------------------------------------------------
1 | "use strict";
2 | // convert to more or less specific
3 | let a = 'hello';
4 | let b = a; // less specific
5 | let c = a; // more specific
6 | let d = 'world';
7 | let e = 'world';
8 | const addOrConcat = (a, b, c) => {
9 | if (c === 'add')
10 | return a + b;
11 | return '' + a + b;
12 | };
13 | let myVal = addOrConcat(2, 2, 'concat');
14 | // Be careful! TS sees no problem - but a string is returned
15 | let nextVal = addOrConcat(2, 2, 'concat');
16 | //10 as string
17 | 10;
18 | // The DOM
19 | const img = document.querySelector('img');
20 | const myImg = document.getElementById('#img');
21 | const nextImg = document.getElementById('#img');
22 | img.src;
23 | myImg.src;
24 |
--------------------------------------------------------------------------------
/lesson05/src/copyright.ts:
--------------------------------------------------------------------------------
1 | // Original JS code
2 | // const year = document.getElementById("year")
3 | // const thisYear = new Date().getFullYear()
4 | // year.setAttribute("datetime", thisYear)
5 | // year.textContent = thisYear
6 |
7 | // 1st variation: (Beginner)
8 | // let year: HTMLElement | null
9 | // year = document.getElementById("year")
10 | // let thisYear: string
11 | // thisYear = new Date().getFullYear().toString()
12 | // if (year) {
13 | // year.setAttribute("datetime", thisYear)
14 | // year.textContent = thisYear
15 | // }
16 |
17 | // 2nd variation: (with Type Assertion)
18 | const year = document.getElementById("year") as HTMLSpanElement
19 | const thisYear: string = new Date().getFullYear().toString()
20 | year.setAttribute("datetime", thisYear)
21 | year.textContent = thisYear
22 |
23 |
--------------------------------------------------------------------------------
/lesson05/src/main.ts:
--------------------------------------------------------------------------------
1 | type One = string
2 | type Two = string | number
3 | type Three = 'hello'
4 |
5 | // convert to more or less specific
6 | let a: One = 'hello'
7 | let b = a as Two // less specific
8 | let c = a as Three // more specific
9 |
10 | let d = 'world'
11 | let e = 'world'
12 |
13 | const addOrConcat = (a: number, b: number, c: 'add' | 'concat'): number | string => {
14 | if (c === 'add') return a + b
15 | return '' + a + b
16 | }
17 |
18 | let myVal: string = addOrConcat(2, 2, 'concat') as string
19 |
20 | // Be careful! TS sees no problem - but a string is returned
21 | let nextVal: number = addOrConcat(2, 2, 'concat') as number
22 |
23 | //10 as string
24 | (10 as unknown) as string
25 |
26 | // The DOM
27 | const img = document.querySelector('img')!
28 | const myImg = document.getElementById('#img') as HTMLImageElement
29 | const nextImg = document.getElementById('#img')
30 |
31 | img.src
32 | myImg.src
--------------------------------------------------------------------------------
/lesson05/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "compilerOptions": {
3 | /* Visit https://aka.ms/tsconfig to read more about this file */
4 | /* Projects */
5 | // "incremental": true, /* Save .tsbuildinfo files to allow for incremental compilation of projects. */
6 | // "composite": true, /* Enable constraints that allow a TypeScript project to be used with project references. */
7 | // "tsBuildInfoFile": "./.tsbuildinfo", /* Specify the path to .tsbuildinfo incremental compilation file. */
8 | // "disableSourceOfProjectReferenceRedirect": true, /* Disable preferring source files instead of declaration files when referencing composite projects. */
9 | // "disableSolutionSearching": true, /* Opt a project out of multi-project reference checking when editing. */
10 | // "disableReferencedProjectLoad": true, /* Reduce the number of projects loaded automatically by TypeScript. */
11 | /* Language and Environment */
12 | "target": "es2016", /* Set the JavaScript language version for emitted JavaScript and include compatible library declarations. */
13 | // "lib": [], /* Specify a set of bundled library declaration files that describe the target runtime environment. */
14 | // "jsx": "preserve", /* Specify what JSX code is generated. */
15 | // "experimentalDecorators": true, /* Enable experimental support for TC39 stage 2 draft decorators. */
16 | // "emitDecoratorMetadata": true, /* Emit design-type metadata for decorated declarations in source files. */
17 | // "jsxFactory": "", /* Specify the JSX factory function used when targeting React JSX emit, e.g. 'React.createElement' or 'h'. */
18 | // "jsxFragmentFactory": "", /* Specify the JSX Fragment reference used for fragments when targeting React JSX emit e.g. 'React.Fragment' or 'Fragment'. */
19 | // "jsxImportSource": "", /* Specify module specifier used to import the JSX factory functions when using 'jsx: react-jsx*'. */
20 | // "reactNamespace": "", /* Specify the object invoked for 'createElement'. This only applies when targeting 'react' JSX emit. */
21 | // "noLib": true, /* Disable including any library files, including the default lib.d.ts. */
22 | // "useDefineForClassFields": true, /* Emit ECMAScript-standard-compliant class fields. */
23 | // "moduleDetection": "auto", /* Control what method is used to detect module-format JS files. */
24 | /* Modules */
25 | "module": "commonjs", /* Specify what module code is generated. */
26 | "rootDir": "./src", /* Specify the root folder within your source files. */
27 | // "moduleResolution": "node", /* Specify how TypeScript looks up a file from a given module specifier. */
28 | // "baseUrl": "./", /* Specify the base directory to resolve non-relative module names. */
29 | // "paths": {}, /* Specify a set of entries that re-map imports to additional lookup locations. */
30 | // "rootDirs": [], /* Allow multiple folders to be treated as one when resolving modules. */
31 | // "typeRoots": [], /* Specify multiple folders that act like './node_modules/@types'. */
32 | // "types": [], /* Specify type package names to be included without being referenced in a source file. */
33 | // "allowUmdGlobalAccess": true, /* Allow accessing UMD globals from modules. */
34 | // "moduleSuffixes": [], /* List of file name suffixes to search when resolving a module. */
35 | // "resolveJsonModule": true, /* Enable importing .json files. */
36 | // "noResolve": true, /* Disallow 'import's, 'require's or ''s from expanding the number of files TypeScript should add to a project. */
37 | /* JavaScript Support */
38 | // "allowJs": true, /* Allow JavaScript files to be a part of your program. Use the 'checkJS' option to get errors from these files. */
39 | // "checkJs": true, /* Enable error reporting in type-checked JavaScript files. */
40 | // "maxNodeModuleJsDepth": 1, /* Specify the maximum folder depth used for checking JavaScript files from 'node_modules'. Only applicable with 'allowJs'. */
41 | /* Emit */
42 | // "declaration": true, /* Generate .d.ts files from TypeScript and JavaScript files in your project. */
43 | // "declarationMap": true, /* Create sourcemaps for d.ts files. */
44 | // "emitDeclarationOnly": true, /* Only output d.ts files and not JavaScript files. */
45 | // "sourceMap": true, /* Create source map files for emitted JavaScript files. */
46 | // "outFile": "./", /* Specify a file that bundles all outputs into one JavaScript file. If 'declaration' is true, also designates a file that bundles all .d.ts output. */
47 | "outDir": "./build/js", /* Specify an output folder for all emitted files. */
48 | // "removeComments": true, /* Disable emitting comments. */
49 | // "noEmit": true, /* Disable emitting files from a compilation. */
50 | // "importHelpers": true, /* Allow importing helper functions from tslib once per project, instead of including them per-file. */
51 | // "importsNotUsedAsValues": "remove", /* Specify emit/checking behavior for imports that are only used for types. */
52 | // "downlevelIteration": true, /* Emit more compliant, but verbose and less performant JavaScript for iteration. */
53 | // "sourceRoot": "", /* Specify the root path for debuggers to find the reference source code. */
54 | // "mapRoot": "", /* Specify the location where debugger should locate map files instead of generated locations. */
55 | // "inlineSourceMap": true, /* Include sourcemap files inside the emitted JavaScript. */
56 | // "inlineSources": true, /* Include source code in the sourcemaps inside the emitted JavaScript. */
57 | // "emitBOM": true, /* Emit a UTF-8 Byte Order Mark (BOM) in the beginning of output files. */
58 | // "newLine": "crlf", /* Set the newline character for emitting files. */
59 | // "stripInternal": true, /* Disable emitting declarations that have '@internal' in their JSDoc comments. */
60 | // "noEmitHelpers": true, /* Disable generating custom helper functions like '__extends' in compiled output. */
61 | // "noEmitOnError": true, /* Disable emitting files if any type checking errors are reported. */
62 | // "preserveConstEnums": true, /* Disable erasing 'const enum' declarations in generated code. */
63 | // "declarationDir": "./", /* Specify the output directory for generated declaration files. */
64 | // "preserveValueImports": true, /* Preserve unused imported values in the JavaScript output that would otherwise be removed. */
65 | /* Interop Constraints */
66 | // "isolatedModules": true, /* Ensure that each file can be safely transpiled without relying on other imports. */
67 | // "allowSyntheticDefaultImports": true, /* Allow 'import x from y' when a module doesn't have a default export. */
68 | "esModuleInterop": true, /* Emit additional JavaScript to ease support for importing CommonJS modules. This enables 'allowSyntheticDefaultImports' for type compatibility. */
69 | // "preserveSymlinks": true, /* Disable resolving symlinks to their realpath. This correlates to the same flag in node. */
70 | "forceConsistentCasingInFileNames": true, /* Ensure that casing is correct in imports. */
71 | /* Type Checking */
72 | "strict": true, /* Enable all strict type-checking options. */
73 | // "noImplicitAny": true, /* Enable error reporting for expressions and declarations with an implied 'any' type. */
74 | // "strictNullChecks": true, /* When type checking, take into account 'null' and 'undefined'. */
75 | // "strictFunctionTypes": true, /* When assigning functions, check to ensure parameters and the return values are subtype-compatible. */
76 | // "strictBindCallApply": true, /* Check that the arguments for 'bind', 'call', and 'apply' methods match the original function. */
77 | // "strictPropertyInitialization": true, /* Check for class properties that are declared but not set in the constructor. */
78 | // "noImplicitThis": true, /* Enable error reporting when 'this' is given the type 'any'. */
79 | // "useUnknownInCatchVariables": true, /* Default catch clause variables as 'unknown' instead of 'any'. */
80 | // "alwaysStrict": true, /* Ensure 'use strict' is always emitted. */
81 | // "noUnusedLocals": true, /* Enable error reporting when local variables aren't read. */
82 | // "noUnusedParameters": true, /* Raise an error when a function parameter isn't read. */
83 | // "exactOptionalPropertyTypes": true, /* Interpret optional property types as written, rather than adding 'undefined'. */
84 | // "noImplicitReturns": true, /* Enable error reporting for codepaths that do not explicitly return in a function. */
85 | // "noFallthroughCasesInSwitch": true, /* Enable error reporting for fallthrough cases in switch statements. */
86 | // "noUncheckedIndexedAccess": true, /* Add 'undefined' to a type when accessed using an index. */
87 | // "noImplicitOverride": true, /* Ensure overriding members in derived classes are marked with an override modifier. */
88 | // "noPropertyAccessFromIndexSignature": true, /* Enforces using indexed accessors for keys declared using an indexed type. */
89 | // "allowUnusedLabels": true, /* Disable error reporting for unused labels. */
90 | // "allowUnreachableCode": true, /* Disable error reporting for unreachable code. */
91 | /* Completeness */
92 | // "skipDefaultLibCheck": true, /* Skip type checking .d.ts files that are included with TypeScript. */
93 | "skipLibCheck": true /* Skip type checking all .d.ts files. */
94 | },
95 | "include": [
96 | "src"
97 | ]
98 | }
--------------------------------------------------------------------------------
/lesson06/build/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 | Example
9 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
--------------------------------------------------------------------------------
/lesson06/build/js/main.js:
--------------------------------------------------------------------------------
1 | "use strict";
2 | class Coder {
3 | constructor(name, music, age, lang = 'Typescript') {
4 | this.name = name;
5 | this.music = music;
6 | this.age = age;
7 | this.lang = lang;
8 | this.name = name;
9 | this.music = music;
10 | this.age = age;
11 | this.lang = lang;
12 | }
13 | getAge() {
14 | return `Hello, I'm ${this.age}`;
15 | }
16 | }
17 | const Dave = new Coder('Dave', 'Rock', 42);
18 | console.log(Dave.getAge());
19 | // console.log(Dave.age)
20 | // console.log(Dave.lang)
21 | class WebDev extends Coder {
22 | constructor(computer, name, music, age) {
23 | super(name, music, age);
24 | this.computer = computer;
25 | this.computer = computer;
26 | }
27 | getLang() {
28 | return `I write ${this.lang}`;
29 | }
30 | }
31 | const Sara = new WebDev('Mac', 'Sara', 'Lofi', 25);
32 | console.log(Sara.getLang());
33 | class Guitarist {
34 | constructor(name, instrument) {
35 | this.name = name;
36 | this.instrument = instrument;
37 | }
38 | play(action) {
39 | return `${this.name} ${action} the ${this.instrument}`;
40 | }
41 | }
42 | const Page = new Guitarist('Jimmy', 'guitar');
43 | console.log(Page.play('strums'));
44 | //////////////////////////////////////
45 | class Peeps {
46 | constructor(name) {
47 | this.name = name;
48 | this.name = name;
49 | this.id = ++Peeps.count;
50 | }
51 | static getCount() {
52 | return Peeps.count;
53 | }
54 | }
55 | Peeps.count = 0;
56 | const John = new Peeps('John');
57 | const Steve = new Peeps('Steve');
58 | const Amy = new Peeps('Amy');
59 | console.log(Amy.id);
60 | console.log(Steve.id);
61 | console.log(John.id);
62 | console.log(Peeps.count);
63 | //////////////////////////////////
64 | class Bands {
65 | constructor() {
66 | this.dataState = [];
67 | }
68 | get data() {
69 | return this.dataState;
70 | }
71 | set data(value) {
72 | if (Array.isArray(value) && value.every(el => typeof el === 'string')) {
73 | this.dataState = value;
74 | return;
75 | }
76 | else
77 | throw new Error('Param is not an array of strings');
78 | }
79 | }
80 | const MyBands = new Bands();
81 | MyBands.data = ['Neil Young', 'Led Zep'];
82 | console.log(MyBands.data);
83 | MyBands.data = [...MyBands.data, 'ZZ Top'];
84 | console.log(MyBands.data);
85 | MyBands.data = ['Van Halen', 5150];
86 |
--------------------------------------------------------------------------------
/lesson06/src/main.ts:
--------------------------------------------------------------------------------
1 | class Coder {
2 |
3 | secondLang!: string
4 |
5 | constructor(
6 | public readonly name: string,
7 | public music: string,
8 | private age: number,
9 | protected lang: string = 'Typescript'
10 | ) {
11 | this.name = name
12 | this.music = music
13 | this.age = age
14 | this.lang = lang
15 | }
16 |
17 | public getAge() {
18 | return `Hello, I'm ${this.age}`
19 | }
20 | }
21 |
22 | const Dave = new Coder('Dave', 'Rock', 42)
23 | console.log(Dave.getAge())
24 | // console.log(Dave.age)
25 | // console.log(Dave.lang)
26 |
27 | class WebDev extends Coder {
28 | constructor(
29 | public computer: string,
30 | name: string,
31 | music: string,
32 | age: number,
33 | ) {
34 | super(name, music, age)
35 | this.computer = computer
36 | }
37 |
38 | public getLang() {
39 | return `I write ${this.lang}`
40 | }
41 | }
42 |
43 | const Sara = new WebDev('Mac', 'Sara', 'Lofi', 25)
44 | console.log(Sara.getLang())
45 | // console.log(Sara.age)
46 | // console.log(Sara.lang)
47 | /////////////////////////////////////
48 |
49 | interface Musician {
50 | name: string,
51 | instrument: string,
52 | play(action: string): string
53 | }
54 |
55 | class Guitarist implements Musician {
56 | name: string
57 | instrument: string
58 |
59 | constructor(name: string, instrument: string) {
60 | this.name = name
61 | this.instrument = instrument
62 | }
63 |
64 | play(action: string) {
65 | return `${this.name} ${action} the ${this.instrument}`
66 | }
67 | }
68 |
69 | const Page = new Guitarist('Jimmy', 'guitar')
70 | console.log(Page.play('strums'))
71 | //////////////////////////////////////
72 |
73 | class Peeps {
74 | static count: number = 0
75 |
76 | static getCount(): number {
77 | return Peeps.count
78 | }
79 |
80 | public id: number
81 |
82 | constructor(public name: string) {
83 | this.name = name
84 | this.id = ++Peeps.count
85 | }
86 | }
87 |
88 | const John = new Peeps('John')
89 | const Steve = new Peeps('Steve')
90 | const Amy = new Peeps('Amy')
91 |
92 | console.log(Amy.id)
93 | console.log(Steve.id)
94 | console.log(John.id)
95 | console.log(Peeps.count)
96 | //////////////////////////////////
97 |
98 | class Bands {
99 | private dataState: string[]
100 |
101 | constructor() {
102 | this.dataState = []
103 | }
104 |
105 | public get data(): string[] {
106 | return this.dataState
107 | }
108 |
109 | public set data(value: string[]) {
110 | if (Array.isArray(value) && value.every(el => typeof el === 'string')) {
111 | this.dataState = value
112 | return
113 | } else throw new Error('Param is not an array of strings')
114 | }
115 | }
116 |
117 | const MyBands = new Bands()
118 | MyBands.data = ['Neil Young', 'Led Zep']
119 | console.log(MyBands.data)
120 | MyBands.data = [...MyBands.data, 'ZZ Top']
121 | console.log(MyBands.data)
122 | MyBands.data = ['Van Halen', 5150] // must be string data
--------------------------------------------------------------------------------
/lesson07/build/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 | Example
9 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
--------------------------------------------------------------------------------
/lesson07/build/js/main.js:
--------------------------------------------------------------------------------
1 | "use strict";
2 | // Index Signatures
3 | const todaysTransactions = {
4 | Pizza: -10,
5 | Books: -5,
6 | Job: 50,
7 | };
8 | console.log(todaysTransactions.Pizza);
9 | console.log(todaysTransactions['Pizza']);
10 | let prop = 'Pizza';
11 | console.log(todaysTransactions[prop]);
12 | const todaysNet = (transactions) => {
13 | let total = 0;
14 | for (const transaction in transactions) {
15 | total += transactions[transaction];
16 | }
17 | return total;
18 | };
19 | console.log(todaysNet(todaysTransactions));
20 | //todaysTransactions.Pizza = 40
21 | console.log(todaysTransactions['Dave']); // undefined
22 | const student = {
23 | name: "Doug",
24 | GPA: 3.5,
25 | classes: [100, 200]
26 | };
27 | // console.log(student.test)
28 | for (const key in student) {
29 | console.log(`${key}: ${student[key]}`);
30 | }
31 | Object.keys(student).map(key => {
32 | console.log(student[key]);
33 | });
34 | const logStudentKey = (student, key) => {
35 | console.log(`Student ${key}: ${student[key]}`);
36 | };
37 | logStudentKey(student, 'name');
38 | const monthlyIncomes = {
39 | salary: 500,
40 | bonus: 100,
41 | sidehustle: 250
42 | };
43 | for (const revenue in monthlyIncomes) {
44 | console.log(monthlyIncomes[revenue]);
45 | }
46 |
--------------------------------------------------------------------------------
/lesson07/src/main.ts:
--------------------------------------------------------------------------------
1 | // Index Signatures
2 |
3 | // interface TransactionObj {
4 | // readonly [index: string]: number
5 | // }
6 |
7 | interface TransactionObj {
8 | readonly [index: string]: number
9 | Pizza: number,
10 | Books: number,
11 | Job: number
12 | }
13 |
14 | const todaysTransactions: TransactionObj = {
15 | Pizza: -10,
16 | Books: -5,
17 | Job: 50,
18 | }
19 |
20 | console.log(todaysTransactions.Pizza)
21 | console.log(todaysTransactions['Pizza'])
22 |
23 | let prop: string = 'Pizza'
24 | console.log(todaysTransactions[prop])
25 |
26 | const todaysNet = (transactions: TransactionObj): number => {
27 | let total = 0
28 | for (const transaction in transactions) {
29 | total += transactions[transaction]
30 | }
31 | return total
32 | }
33 |
34 | console.log(todaysNet(todaysTransactions))
35 |
36 | //todaysTransactions.Pizza = 40
37 |
38 | console.log(todaysTransactions['Dave']) // undefined
39 |
40 | ///////////////////////////////////
41 |
42 | interface Student {
43 | //[key: string]: string | number | number[] | undefined
44 | name: string,
45 | GPA: number,
46 | classes?: number[]
47 | }
48 |
49 | const student: Student = {
50 | name: "Doug",
51 | GPA: 3.5,
52 | classes: [100, 200]
53 | }
54 |
55 | // console.log(student.test)
56 |
57 | for (const key in student) {
58 | console.log(`${key}: ${student[key as keyof Student]}`)
59 | }
60 |
61 | Object.keys(student).map(key => {
62 | console.log(student[key as keyof typeof student])
63 | })
64 |
65 | const logStudentKey = (student: Student, key: keyof Student): void => {
66 | console.log(`Student ${key}: ${student[key]}`)
67 | }
68 |
69 | logStudentKey(student, 'name')
70 |
71 | /////////////////////////////////
72 |
73 | // interface Incomes {
74 | // [key: string]: number
75 | // }
76 |
77 | type Streams = 'salary' | 'bonus' | 'sidehustle'
78 |
79 | type Incomes = Record
80 |
81 | const monthlyIncomes: Incomes = {
82 | salary: 500,
83 | bonus: 100,
84 | sidehustle: 250
85 | }
86 |
87 | for (const revenue in monthlyIncomes) {
88 | console.log(monthlyIncomes[revenue as keyof Incomes])
89 | }
--------------------------------------------------------------------------------
/lesson08/build/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 | Example
9 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
--------------------------------------------------------------------------------
/lesson08/build/js/main.js:
--------------------------------------------------------------------------------
1 | "use strict";
2 | const echo = (arg) => arg;
3 | const isObj = (arg) => {
4 | return (typeof arg === 'object' && !Array.isArray(arg) && arg !== null);
5 | };
6 | console.log(isObj(true));
7 | console.log(isObj('John'));
8 | console.log(isObj([1, 2, 3]));
9 | console.log(isObj({ name: 'John' }));
10 | console.log(isObj(null));
11 | const isTrue = (arg) => {
12 | if (Array.isArray(arg) && !arg.length) {
13 | return { arg, is: false };
14 | }
15 | if (isObj(arg) && !Object.keys(arg).length) {
16 | return { arg, is: false };
17 | }
18 | return { arg, is: !!arg };
19 | };
20 | console.log(isTrue(false));
21 | console.log(isTrue(0));
22 | console.log(isTrue(true));
23 | console.log(isTrue(1));
24 | console.log(isTrue('Dave'));
25 | console.log(isTrue(''));
26 | console.log(isTrue(null));
27 | console.log(isTrue(undefined));
28 | console.log(isTrue({})); // modified
29 | console.log(isTrue({ name: 'Dave' }));
30 | console.log(isTrue([])); // modified
31 | console.log(isTrue([1, 2, 3]));
32 | console.log(isTrue(NaN));
33 | console.log(isTrue(-0));
34 | const checkBoolValue = (arg) => {
35 | if (Array.isArray(arg) && !arg.length) {
36 | return { value: arg, is: false };
37 | }
38 | if (isObj(arg) && !Object.keys(arg).length) {
39 | return { value: arg, is: false };
40 | }
41 | return { value: arg, is: !!arg };
42 | };
43 | const processUser = (user) => {
44 | // process the user with logic here
45 | return user;
46 | };
47 | console.log(processUser({ id: 1, name: 'Dave' }));
48 | //console.log(processUser({ name: 'Dave'}))
49 | const getUsersProperty = (users, key) => {
50 | return users.map(user => user[key]);
51 | };
52 | const usersArray = [
53 | {
54 | "id": 1,
55 | "name": "Leanne Graham",
56 | "username": "Bret",
57 | "email": "Sincere@april.biz",
58 | "address": {
59 | "street": "Kulas Light",
60 | "suite": "Apt. 556",
61 | "city": "Gwenborough",
62 | "zipcode": "92998-3874",
63 | "geo": {
64 | "lat": "-37.3159",
65 | "lng": "81.1496"
66 | }
67 | },
68 | "phone": "1-770-736-8031 x56442",
69 | "website": "hildegard.org",
70 | "company": {
71 | "name": "Romaguera-Crona",
72 | "catchPhrase": "Multi-layered client-server neural-net",
73 | "bs": "harness real-time e-markets"
74 | }
75 | },
76 | {
77 | "id": 2,
78 | "name": "Ervin Howell",
79 | "username": "Antonette",
80 | "email": "Shanna@melissa.tv",
81 | "address": {
82 | "street": "Victor Plains",
83 | "suite": "Suite 879",
84 | "city": "Wisokyburgh",
85 | "zipcode": "90566-7771",
86 | "geo": {
87 | "lat": "-43.9509",
88 | "lng": "-34.4618"
89 | }
90 | },
91 | "phone": "010-692-6593 x09125",
92 | "website": "anastasia.net",
93 | "company": {
94 | "name": "Deckow-Crist",
95 | "catchPhrase": "Proactive didactic contingency",
96 | "bs": "synergize scalable supply-chains"
97 | }
98 | },
99 | ];
100 | console.log(getUsersProperty(usersArray, "email"));
101 | console.log(getUsersProperty(usersArray, "username"));
102 | class StateObject {
103 | constructor(value) {
104 | this.data = value;
105 | }
106 | get state() {
107 | return this.data;
108 | }
109 | set state(value) {
110 | this.data = value;
111 | }
112 | }
113 | const store = new StateObject("John");
114 | console.log(store.state);
115 | store.state = "Dave";
116 | //store.state = 12
117 | const myState = new StateObject([15]);
118 | myState.state = ['Dave', 42, true];
119 | console.log(myState.state);
120 |
--------------------------------------------------------------------------------
/lesson08/src/main.ts:
--------------------------------------------------------------------------------
1 | const echo = (arg: T): T => arg
2 |
3 | //////////////////////////////////
4 |
5 | const isObj = (arg: T): boolean => {
6 | return (typeof arg === 'object' && !Array.isArray(arg) && arg !== null)
7 | }
8 |
9 | console.log(isObj(true))
10 | console.log(isObj('John'))
11 | console.log(isObj([1, 2, 3]))
12 | console.log(isObj({ name: 'John' }))
13 | console.log(isObj(null))
14 |
15 | ///////////////////////////////////
16 |
17 | const isTrue = (arg: T): { arg: T, is: boolean } => {
18 | if (Array.isArray(arg) && !arg.length) {
19 | return { arg, is: false }
20 | }
21 | if (isObj(arg) && !Object.keys(arg as keyof T).length) {
22 | return { arg, is: false }
23 | }
24 | return { arg, is: !!arg }
25 | }
26 |
27 | console.log(isTrue(false))
28 | console.log(isTrue(0))
29 | console.log(isTrue(true))
30 | console.log(isTrue(1))
31 | console.log(isTrue('Dave'))
32 | console.log(isTrue(''))
33 | console.log(isTrue(null))
34 | console.log(isTrue(undefined))
35 | console.log(isTrue({})) // modified
36 | console.log(isTrue({ name: 'Dave' }))
37 | console.log(isTrue([])) // modified
38 | console.log(isTrue([1, 2, 3]))
39 | console.log(isTrue(NaN))
40 | console.log(isTrue(-0))
41 |
42 | ////////////////////////////////////
43 |
44 | interface BoolCheck {
45 | value: T,
46 | is: boolean,
47 | }
48 |
49 | const checkBoolValue = (arg: T): BoolCheck => {
50 | if (Array.isArray(arg) && !arg.length) {
51 | return { value: arg, is: false }
52 | }
53 | if (isObj(arg) && !Object.keys(arg as keyof T).length) {
54 | return { value: arg, is: false }
55 | }
56 | return { value: arg, is: !!arg }
57 | }
58 |
59 | //////////////////////////////////////
60 |
61 |
62 | interface HasID {
63 | id: number
64 | }
65 |
66 | const processUser = (user: T): T => {
67 | // process the user with logic here
68 | return user
69 | }
70 |
71 | console.log(processUser({ id: 1, name: 'Dave' }))
72 | //console.log(processUser({ name: 'Dave'}))
73 |
74 | ///////////////////////////////////////
75 |
76 |
77 | const getUsersProperty = (users: T[], key: K): T[K][] => {
78 | return users.map(user => user[key])
79 | }
80 |
81 | const usersArray = [
82 | {
83 | "id": 1,
84 | "name": "Leanne Graham",
85 | "username": "Bret",
86 | "email": "Sincere@april.biz",
87 | "address": {
88 | "street": "Kulas Light",
89 | "suite": "Apt. 556",
90 | "city": "Gwenborough",
91 | "zipcode": "92998-3874",
92 | "geo": {
93 | "lat": "-37.3159",
94 | "lng": "81.1496"
95 | }
96 | },
97 | "phone": "1-770-736-8031 x56442",
98 | "website": "hildegard.org",
99 | "company": {
100 | "name": "Romaguera-Crona",
101 | "catchPhrase": "Multi-layered client-server neural-net",
102 | "bs": "harness real-time e-markets"
103 | }
104 | },
105 | {
106 | "id": 2,
107 | "name": "Ervin Howell",
108 | "username": "Antonette",
109 | "email": "Shanna@melissa.tv",
110 | "address": {
111 | "street": "Victor Plains",
112 | "suite": "Suite 879",
113 | "city": "Wisokyburgh",
114 | "zipcode": "90566-7771",
115 | "geo": {
116 | "lat": "-43.9509",
117 | "lng": "-34.4618"
118 | }
119 | },
120 | "phone": "010-692-6593 x09125",
121 | "website": "anastasia.net",
122 | "company": {
123 | "name": "Deckow-Crist",
124 | "catchPhrase": "Proactive didactic contingency",
125 | "bs": "synergize scalable supply-chains"
126 | }
127 | },
128 | ]
129 |
130 | console.log(getUsersProperty(usersArray, "email"))
131 | console.log(getUsersProperty(usersArray, "username"))
132 |
133 | ///////////////////////////////////////
134 |
135 | class StateObject {
136 | private data: T
137 |
138 | constructor(value: T) {
139 | this.data = value
140 | }
141 |
142 | get state(): T {
143 | return this.data
144 | }
145 |
146 | set state(value: T) {
147 | this.data = value
148 | }
149 | }
150 |
151 | const store = new StateObject("John")
152 | console.log(store.state)
153 | store.state = "Dave"
154 | //store.state = 12
155 |
156 | const myState = new StateObject<(string | number | boolean)[]>([15])
157 | myState.state = ['Dave', 42, true]
158 | console.log(myState.state)
--------------------------------------------------------------------------------
/lesson09/build/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 | Example
9 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
--------------------------------------------------------------------------------
/lesson09/build/js/main.js:
--------------------------------------------------------------------------------
1 | "use strict";
2 | // Utility Types
3 | var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
4 | function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
5 | return new (P || (P = Promise))(function (resolve, reject) {
6 | function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
7 | function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
8 | function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
9 | step((generator = generator.apply(thisArg, _arguments || [])).next());
10 | });
11 | };
12 | const updateAssignment = (assign, propsToUpdate) => {
13 | return Object.assign(Object.assign({}, assign), propsToUpdate);
14 | };
15 | const assign1 = {
16 | studentId: "compsci123",
17 | title: "Final Project",
18 | grade: 0,
19 | };
20 | console.log(updateAssignment(assign1, { grade: 95 }));
21 | const assignGraded = updateAssignment(assign1, { grade: 95 });
22 | // Required and Readonly
23 | const recordAssignment = (assign) => {
24 | // send to database, etc.
25 | return assign;
26 | };
27 | const assignVerified = Object.assign(Object.assign({}, assignGraded), { verified: true });
28 | recordAssignment(Object.assign(Object.assign({}, assignGraded), { verified: true }));
29 | // Record
30 | const hexColorMap = {
31 | red: "FF0000",
32 | green: "00FF00",
33 | blue: "0000FF",
34 | };
35 | const finalGrades = {
36 | Sara: "B",
37 | Kelly: "U"
38 | };
39 | const gradeData = {
40 | Sara: { assign1: 85, assign2: 93 },
41 | Kelly: { assign1: 76, assign2: 15 },
42 | };
43 | const score = {
44 | studentId: "k123",
45 | grade: 85,
46 | };
47 | const preview = {
48 | studentId: "k123",
49 | title: "Final Project",
50 | };
51 | // ReturnType
52 | //type newAssign = { title: string, points: number }
53 | const createNewAssign = (title, points) => {
54 | return { title, points };
55 | };
56 | const tsAssign = createNewAssign("Utility Types", 100);
57 | console.log(tsAssign);
58 | const assignArgs = ["Generics", 100];
59 | const tsAssign2 = createNewAssign(...assignArgs);
60 | console.log(tsAssign2);
61 | const fetchUsers = () => __awaiter(void 0, void 0, void 0, function* () {
62 | const data = yield fetch('https://jsonplaceholder.typicode.com/users').then(res => {
63 | return res.json();
64 | }).catch(err => {
65 | if (err instanceof Error)
66 | console.log(err.message);
67 | });
68 | return data;
69 | });
70 | fetchUsers().then(users => console.log(users));
71 |
--------------------------------------------------------------------------------
/lesson09/src/main.ts:
--------------------------------------------------------------------------------
1 | // Utility Types
2 |
3 | // Partial
4 |
5 | interface Assignment {
6 | studentId: string,
7 | title: string,
8 | grade: number,
9 | verified?: boolean,
10 | }
11 |
12 | const updateAssignment = (assign: Assignment, propsToUpdate: Partial): Assignment => {
13 | return { ...assign, ...propsToUpdate }
14 | }
15 |
16 | const assign1: Assignment = {
17 | studentId: "compsci123",
18 | title: "Final Project",
19 | grade: 0,
20 | }
21 |
22 | console.log(updateAssignment(assign1, { grade: 95 }))
23 | const assignGraded: Assignment = updateAssignment(assign1, { grade: 95 })
24 |
25 |
26 | // Required and Readonly
27 |
28 | const recordAssignment = (assign: Required): Assignment => {
29 | // send to database, etc.
30 | return assign
31 | }
32 |
33 | const assignVerified: Readonly = { ...assignGraded, verified: true }
34 |
35 | // NOTE: assignVerified won't work with recordAssignment!
36 | // Why? Try it and see what TS tells you :)
37 |
38 | recordAssignment({ ...assignGraded, verified: true })
39 |
40 | // Record
41 | const hexColorMap: Record = {
42 | red: "FF0000",
43 | green: "00FF00",
44 | blue: "0000FF",
45 | }
46 |
47 | type Students = "Sara" | "Kelly"
48 | type LetterGrades = "A" | "B" | "C" | "D" | "U"
49 |
50 | const finalGrades: Record = {
51 | Sara: "B",
52 | Kelly: "U"
53 | }
54 |
55 | interface Grades {
56 | assign1: number,
57 | assign2: number,
58 | }
59 |
60 | const gradeData: Record = {
61 | Sara: { assign1: 85, assign2: 93 },
62 | Kelly: { assign1: 76, assign2: 15 },
63 | }
64 |
65 | // Pick and Omit
66 |
67 | type AssignResult = Pick
68 |
69 | const score: AssignResult = {
70 | studentId: "k123",
71 | grade: 85,
72 | }
73 |
74 | type AssignPreview = Omit
75 |
76 | const preview: AssignPreview = {
77 | studentId: "k123",
78 | title: "Final Project",
79 | }
80 |
81 | // Exclude and Extract
82 |
83 | type adjustedGrade = Exclude
84 |
85 | type highGrades = Extract
86 |
87 | // Nonnullable
88 |
89 | type AllPossibleGrades = 'Dave' | 'John' | null | undefined
90 | type NamesOnly = NonNullable
91 |
92 | // ReturnType
93 |
94 | //type newAssign = { title: string, points: number }
95 |
96 | const createNewAssign = (title: string, points: number) => {
97 | return { title, points }
98 | }
99 |
100 | type NewAssign = ReturnType
101 |
102 | const tsAssign: NewAssign = createNewAssign("Utility Types", 100)
103 | console.log(tsAssign)
104 |
105 | // Parameters
106 |
107 | type AssignParams = Parameters
108 |
109 | const assignArgs: AssignParams = ["Generics", 100]
110 |
111 | const tsAssign2: NewAssign = createNewAssign(...assignArgs)
112 | console.log(tsAssign2)
113 |
114 | // Awaited - helps us with the ReturnType of a Promise
115 |
116 | interface User {
117 | id: number,
118 | name: string,
119 | username: string,
120 | email: string,
121 | }
122 |
123 | const fetchUsers = async (): Promise => {
124 |
125 | const data = await fetch(
126 | 'https://jsonplaceholder.typicode.com/users'
127 | ).then(res => {
128 | return res.json()
129 | }).catch(err => {
130 | if (err instanceof Error) console.log(err.message)
131 | })
132 | return data
133 | }
134 |
135 | type FetchUsersReturnType = Awaited>
136 |
137 | fetchUsers().then(users => console.log(users))
--------------------------------------------------------------------------------
/lesson11/.gitignore:
--------------------------------------------------------------------------------
1 | # Logs
2 | logs
3 | *.log
4 | npm-debug.log*
5 | yarn-debug.log*
6 | yarn-error.log*
7 | pnpm-debug.log*
8 | lerna-debug.log*
9 |
10 | node_modules
11 | dist
12 | dist-ssr
13 | *.local
14 |
15 | # Editor directories and files
16 | .vscode/*
17 | !.vscode/extensions.json
18 | .idea
19 | .DS_Store
20 | *.suo
21 | *.ntvs*
22 | *.njsproj
23 | *.sln
24 | *.sw?
25 |
--------------------------------------------------------------------------------
/lesson11/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 | My List
9 |
10 |
11 |
12 |
13 |
14 |
15 | My List
16 |
17 |
18 | New Item Entry
19 |
27 |
28 |
29 |
30 |
31 | List
32 |
36 |
37 |
38 |
55 |
56 |
57 |
58 |
59 |
--------------------------------------------------------------------------------
/lesson11/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "lesson11",
3 | "private": true,
4 | "version": "0.0.0",
5 | "type": "module",
6 | "scripts": {
7 | "dev": "vite",
8 | "build": "tsc && vite build",
9 | "preview": "vite preview"
10 | },
11 | "devDependencies": {
12 | "typescript": "^4.6.4",
13 | "vite": "^3.2.3"
14 | }
15 | }
--------------------------------------------------------------------------------
/lesson11/src/css/style.css:
--------------------------------------------------------------------------------
1 | @import url('https://fonts.googleapis.com/css2?family=Poppins&display=swap');
2 |
3 | * {
4 | margin: 0;
5 | padding: 0;
6 | box-sizing: border-box;
7 | }
8 |
9 | .offscreen {
10 | position: absolute;
11 | left: -10000px;
12 | }
13 |
14 | input,
15 | button {
16 | font: inherit;
17 | }
18 |
19 | html {
20 | font-family: 'Poppins', sans-serif;
21 | }
22 |
23 | body {
24 | min-height: 100vh;
25 | background-color: #333;
26 | color: #fff;
27 | padding: 1rem;
28 | display: flex;
29 | flex-direction: column;
30 | }
31 |
32 | main {
33 | flex-grow: 1;
34 | margin: auto;
35 | width: 100%;
36 | max-width: 800px;
37 | display: flex;
38 | flex-flow: column nowrap;
39 | }
40 |
41 | section {
42 | border: 1px solid whitesmoke;
43 | border-radius: 10px;
44 | padding: 0.5rem;
45 | }
46 |
47 | .button {
48 | border-radius: 10px;
49 | min-width: 48px;
50 | min-height: 48px;
51 | }
52 |
53 | .button:hover {
54 | cursor: pointer;
55 | }
56 |
57 | .newItemEntry {
58 | position: sticky;
59 | top: 0;
60 | margin-bottom: 1rem;
61 | }
62 |
63 | .newItemEntry__form {
64 | display: flex;
65 | gap: 0.25rem;
66 | font-size: 1.5rem;
67 | }
68 |
69 | .newItemEntry__input {
70 | width: calc(100% - (0.25rem + 48px));
71 | flex-grow: 1;
72 | border: 2px solid whitesmoke;
73 | border-radius: 10px;
74 | padding: 0.5em;
75 | }
76 |
77 | .newItemEntry__button {
78 | background-color: transparent;
79 | color: whitesmoke;
80 | border: 3px dashed whitesmoke;
81 | padding: 0.75em;
82 | }
83 |
84 | .newItemEntry__button:hover,
85 | .newItemEntry__button:focus {
86 | color: limegreen;
87 | }
88 |
89 | .listContainer {
90 | font-size: 1.5rem;
91 | flex-grow: 1;
92 | display: flex;
93 | flex-flow: column;
94 | gap: 1rem;
95 | }
96 |
97 | .listTitle {
98 | display: flex;
99 | justify-content: space-between;
100 | align-items: flex-end;
101 | }
102 |
103 | .listTitle__button {
104 | background-color: transparent;
105 | color: whitesmoke;
106 | padding: 0.25em;
107 | }
108 |
109 |
110 | .listItems {
111 | flex-grow: 1;
112 | display: flex;
113 | flex-flow: column nowrap;
114 | list-style-type: none;
115 | }
116 |
117 | .item {
118 | display: flex;
119 | align-items: center;
120 | padding-top: 1em;
121 | gap: 1em;
122 | }
123 |
124 | .item > input[type="checkbox"] {
125 | text-align: center;
126 | min-width: 2.5rem;
127 | min-height: 2.5rem;
128 | cursor: pointer;
129 | }
130 |
131 | .item > input[type="checkbox"]:checked + label {
132 | text-decoration: line-through;
133 | }
134 |
135 | .item > label {
136 | flex-grow: 1;
137 | word-break: break-all;
138 | }
139 |
140 | .item > button:hover,
141 | .item > button:focus {
142 | color: red;
143 | }
144 |
145 | @media (min-width: 768px) {
146 | section {
147 | padding: 1rem;
148 | }
149 | .newItemEntry__form {
150 | gap: 0.5rem;
151 | }
152 | }
--------------------------------------------------------------------------------
/lesson11/src/main.ts:
--------------------------------------------------------------------------------
1 | import './css/style.css'
2 | import FullList from './model/FullList'
3 | import ListItem from './model/ListItem'
4 | import ListTemplate from './templates/ListTemplate'
5 |
6 | const initApp = (): void => {
7 | const fullList = FullList.instance
8 | const template = ListTemplate.instance
9 |
10 | // Add listener to new entry form submit
11 | const itemEntryForm = document.getElementById("itemEntryForm") as HTMLFormElement
12 |
13 | itemEntryForm.addEventListener("submit", (event: SubmitEvent): void => {
14 | event.preventDefault()
15 |
16 | // Get the new item value
17 | const input = document.getElementById("newItem") as HTMLInputElement
18 | const newEntryText: string = input.value.trim()
19 | if (!newEntryText.length) return
20 |
21 | // calculate item ID
22 | const itemId: number = fullList.list.length
23 | ? parseInt(fullList.list[fullList.list.length - 1].id) + 1
24 | : 1
25 |
26 | // create new item
27 | const newItem = new ListItem(itemId.toString(), newEntryText)
28 | // Add new item to full list
29 | fullList.addItem(newItem)
30 | // Re-render list with new item included
31 | template.render(fullList)
32 | })
33 |
34 | // Add listener to "Clear" button
35 | const clearItems = document.getElementById("clearItemsButton") as HTMLButtonElement
36 |
37 | clearItems.addEventListener('click', (): void => {
38 | fullList.clearList()
39 | template.clear()
40 | })
41 |
42 | // load initial data
43 | fullList.load()
44 | // initial render of template
45 | template.render(fullList)
46 | }
47 |
48 | document.addEventListener("DOMContentLoaded", initApp)
--------------------------------------------------------------------------------
/lesson11/src/model/FullList.ts:
--------------------------------------------------------------------------------
1 | import ListItem from './ListItem'
2 |
3 | interface List {
4 | list: ListItem[],
5 | load(): void,
6 | save(): void,
7 | clearList(): void,
8 | addItem(itemObj: ListItem): void,
9 | removeItem(id: string): void,
10 | }
11 |
12 | export default class FullList implements List {
13 |
14 | static instance: FullList = new FullList()
15 |
16 | private constructor(private _list: ListItem[] = []) { }
17 |
18 | get list(): ListItem[] {
19 | return this._list
20 | }
21 |
22 | load(): void {
23 | const storedList: string | null = localStorage.getItem("myList")
24 | if (typeof storedList !== "string") return
25 |
26 | const parsedList: { _id: string, _item: string, _checked: boolean }[] = JSON.parse(storedList)
27 |
28 | parsedList.forEach(itemObj => {
29 | const newListItem = new ListItem(itemObj._id, itemObj._item, itemObj._checked)
30 | FullList.instance.addItem(newListItem)
31 | })
32 | }
33 |
34 | save(): void {
35 | localStorage.setItem("myList", JSON.stringify(this._list))
36 | }
37 |
38 | clearList(): void {
39 | this._list = []
40 | this.save()
41 | }
42 |
43 | addItem(itemObj: ListItem): void {
44 | this._list.push(itemObj)
45 | this.save()
46 | }
47 |
48 | removeItem(id: string): void {
49 | this._list = this._list.filter(item => item.id !== id)
50 | this.save()
51 | }
52 | }
--------------------------------------------------------------------------------
/lesson11/src/model/ListItem.ts:
--------------------------------------------------------------------------------
1 | export interface Item {
2 | id: string,
3 | item: string,
4 | checked: boolean,
5 | }
6 |
7 | export default class ListItem implements Item {
8 |
9 | constructor(
10 | private _id: string = '',
11 | private _item: string = '',
12 | private _checked: boolean = false,
13 | ) { }
14 |
15 | get id(): string {
16 | return this._id
17 | }
18 |
19 | set id(id: string) {
20 | this._id = id
21 | }
22 |
23 | get item(): string {
24 | return this._item
25 | }
26 |
27 | set item(item: string) {
28 | this._item = item
29 | }
30 |
31 | get checked(): boolean {
32 | return this._checked
33 | }
34 |
35 | set checked(checked: boolean) {
36 | this._checked = checked
37 | }
38 | }
--------------------------------------------------------------------------------
/lesson11/src/templates/ListTemplate.ts:
--------------------------------------------------------------------------------
1 | import FullList from "../model/FullList"
2 |
3 | interface DOMList {
4 | ul: HTMLUListElement,
5 | clear(): void,
6 | render(fullList: FullList): void,
7 | }
8 |
9 | export default class ListTemplate implements DOMList {
10 |
11 | ul: HTMLUListElement
12 |
13 | static instance: ListTemplate = new ListTemplate()
14 |
15 | private constructor() {
16 | this.ul = document.getElementById("listItems") as HTMLUListElement
17 | }
18 |
19 | clear(): void {
20 | this.ul.innerHTML = ''
21 | }
22 |
23 | render(fullList: FullList): void {
24 | this.clear()
25 |
26 | fullList.list.forEach(item => {
27 | const li = document.createElement("li") as HTMLLIElement
28 | li.className = "item"
29 |
30 | const check = document.createElement("input") as HTMLInputElement
31 | check.type = "checkbox"
32 | check.id = item.id
33 | check.checked = item.checked
34 | li.append(check)
35 |
36 | check.addEventListener('change', () => {
37 | item.checked = !item.checked
38 | fullList.save()
39 | })
40 |
41 | const label = document.createElement("label") as HTMLLabelElement
42 | label.htmlFor = item.id
43 | label.textContent = item.item
44 | li.append(label)
45 |
46 | const button = document.createElement("button") as HTMLButtonElement
47 | button.className = 'button'
48 | button.textContent = 'X'
49 | li.append(button)
50 |
51 | button.addEventListener('click', () => {
52 | fullList.removeItem(item.id)
53 | this.render(fullList)
54 | })
55 |
56 | this.ul.append(li)
57 | })
58 | }
59 | }
--------------------------------------------------------------------------------
/lesson11/src/vite-env.d.ts:
--------------------------------------------------------------------------------
1 | ///
2 |
--------------------------------------------------------------------------------
/lesson11/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "compilerOptions": {
3 | "target": "ESNext",
4 | "useDefineForClassFields": true,
5 | "module": "ESNext",
6 | "lib": ["ESNext", "DOM"],
7 | "moduleResolution": "Node",
8 | "strict": true,
9 | "resolveJsonModule": true,
10 | "isolatedModules": true,
11 | "esModuleInterop": true,
12 | "noEmit": true,
13 | "noUnusedLocals": true,
14 | "noUnusedParameters": true,
15 | "noImplicitReturns": true,
16 | "skipLibCheck": true
17 | },
18 | "include": ["src"]
19 | }
20 |
--------------------------------------------------------------------------------
/lesson12/.gitignore:
--------------------------------------------------------------------------------
1 | # Logs
2 | logs
3 | *.log
4 | npm-debug.log*
5 | yarn-debug.log*
6 | yarn-error.log*
7 | pnpm-debug.log*
8 | lerna-debug.log*
9 |
10 | node_modules
11 | dist
12 | dist-ssr
13 | *.local
14 |
15 | # Editor directories and files
16 | .vscode/*
17 | !.vscode/extensions.json
18 | .idea
19 | .DS_Store
20 | *.suo
21 | *.ntvs*
22 | *.njsproj
23 | *.sln
24 | *.sw?
25 |
--------------------------------------------------------------------------------
/lesson12/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 | Vite + React + TS
8 |
9 |
10 |
11 |
12 |
13 |
14 |
--------------------------------------------------------------------------------
/lesson12/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "lesson12",
3 | "private": true,
4 | "version": "0.0.0",
5 | "type": "module",
6 | "scripts": {
7 | "dev": "vite",
8 | "build": "tsc && vite build",
9 | "preview": "vite preview"
10 | },
11 | "dependencies": {
12 | "react": "^18.2.0",
13 | "react-dom": "^18.2.0"
14 | },
15 | "devDependencies": {
16 | "@types/react": "^18.0.24",
17 | "@types/react-dom": "^18.0.8",
18 | "@vitejs/plugin-react": "^2.2.0",
19 | "typescript": "^4.6.4",
20 | "vite": "^3.2.3"
21 | }
22 | }
--------------------------------------------------------------------------------
/lesson12/public/vite.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/lesson12/src/App.tsx:
--------------------------------------------------------------------------------
1 | import Heading from "./components/Heading"
2 | import { Section } from "./components/Section"
3 | import Counter from "./components/Counter"
4 | import List from "./components/List"
5 |
6 | import { useState } from 'react'
7 |
8 | function App() {
9 | const [count, setCount] = useState(1)
10 |
11 | return (
12 | <>
13 |
14 |
15 | Count is {count}
16 | {item}} />
17 | >
18 | )
19 | }
20 |
21 | export default App
22 |
--------------------------------------------------------------------------------
/lesson12/src/assets/react.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/lesson12/src/components/Counter.tsx:
--------------------------------------------------------------------------------
1 | import { ReactNode } from 'react'
2 |
3 | type CounterProps = {
4 | setCount: React.Dispatch>,
5 | children: ReactNode,
6 | }
7 |
8 | const Counter = ({ setCount, children }: CounterProps) => {
9 |
10 | return (
11 | <>
12 | {children}
13 |
14 |
15 | >
16 | )
17 | }
18 | export default Counter
--------------------------------------------------------------------------------
/lesson12/src/components/Heading.tsx:
--------------------------------------------------------------------------------
1 | import { ReactElement } from "react"
2 |
3 | type HeadingProps = { title: string }
4 |
5 | const Heading = ({ title }: HeadingProps): ReactElement => {
6 | return {title}
7 | }
8 | export default Heading
--------------------------------------------------------------------------------
/lesson12/src/components/List.tsx:
--------------------------------------------------------------------------------
1 | import { ReactNode } from 'react'
2 |
3 | interface ListProps {
4 | items: T[],
5 | render: (item: T) => ReactNode
6 | }
7 |
8 | const List = ({ items, render }: ListProps) => {
9 | return (
10 |
11 | {items.map((item, i) => (
12 | -
13 | {render(item)}
14 |
15 | ))}
16 |
17 | )
18 | }
19 | export default List
--------------------------------------------------------------------------------
/lesson12/src/components/Section.tsx:
--------------------------------------------------------------------------------
1 | import { ReactNode } from "react"
2 |
3 | type SectionProps = {
4 | title?: string,
5 | children: ReactNode
6 | }
7 |
8 | export const Section = ({ children, title = "My Subheading" }: SectionProps) => {
9 | return (
10 |
11 | {title}
12 | {children}
13 |
14 | )
15 | }
16 |
--------------------------------------------------------------------------------
/lesson12/src/index.css:
--------------------------------------------------------------------------------
1 | * {
2 | margin: 0;
3 | padding: 0.25rem;
4 | box-sizing: border-box;
5 | }
6 |
7 | body {
8 | background-color: #333;
9 | color: whitesmoke;
10 | display: grid;
11 | place-content: center;
12 | font-size: 150%;
13 | }
14 |
15 | button {
16 | font-size: 2rem;
17 | }
18 |
19 | .bold {
20 | font-weight: bold;
21 | }
22 |
23 | .gold {
24 | color: gold;
25 | }
--------------------------------------------------------------------------------
/lesson12/src/main.tsx:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 | import ReactDOM from 'react-dom/client'
3 | import App from './App'
4 | import './index.css'
5 |
6 | ReactDOM.createRoot(document.getElementById('root') as HTMLElement).render(
7 |
8 |
9 |
10 | )
11 |
--------------------------------------------------------------------------------
/lesson12/src/vite-env.d.ts:
--------------------------------------------------------------------------------
1 | ///
2 |
--------------------------------------------------------------------------------
/lesson12/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "compilerOptions": {
3 | "target": "ESNext",
4 | "useDefineForClassFields": true,
5 | "lib": ["DOM", "DOM.Iterable", "ESNext"],
6 | "allowJs": false,
7 | "skipLibCheck": true,
8 | "esModuleInterop": false,
9 | "allowSyntheticDefaultImports": true,
10 | "strict": true,
11 | "forceConsistentCasingInFileNames": true,
12 | "module": "ESNext",
13 | "moduleResolution": "Node",
14 | "resolveJsonModule": true,
15 | "isolatedModules": true,
16 | "noEmit": true,
17 | "jsx": "react-jsx"
18 | },
19 | "include": ["src"],
20 | "references": [{ "path": "./tsconfig.node.json" }]
21 | }
22 |
--------------------------------------------------------------------------------
/lesson12/tsconfig.node.json:
--------------------------------------------------------------------------------
1 | {
2 | "compilerOptions": {
3 | "composite": true,
4 | "module": "ESNext",
5 | "moduleResolution": "Node",
6 | "allowSyntheticDefaultImports": true
7 | },
8 | "include": ["vite.config.ts"]
9 | }
10 |
--------------------------------------------------------------------------------
/lesson12/vite.config.ts:
--------------------------------------------------------------------------------
1 | import { defineConfig } from 'vite'
2 | import react from '@vitejs/plugin-react'
3 |
4 | // https://vitejs.dev/config/
5 | export default defineConfig({
6 | plugins: [react()]
7 | })
8 |
--------------------------------------------------------------------------------
/lesson13/.gitignore:
--------------------------------------------------------------------------------
1 | # Logs
2 | logs
3 | *.log
4 | npm-debug.log*
5 | yarn-debug.log*
6 | yarn-error.log*
7 | pnpm-debug.log*
8 | lerna-debug.log*
9 |
10 | node_modules
11 | dist
12 | dist-ssr
13 | *.local
14 |
15 | # Editor directories and files
16 | .vscode/*
17 | !.vscode/extensions.json
18 | .idea
19 | .DS_Store
20 | *.suo
21 | *.ntvs*
22 | *.njsproj
23 | *.sln
24 | *.sw?
25 |
--------------------------------------------------------------------------------
/lesson13/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 | Vite + React + TS
8 |
9 |
10 |
11 |
12 |
13 |
14 |
--------------------------------------------------------------------------------
/lesson13/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "lesson13",
3 | "private": true,
4 | "version": "0.0.0",
5 | "type": "module",
6 | "scripts": {
7 | "dev": "vite",
8 | "build": "tsc && vite build",
9 | "preview": "vite preview"
10 | },
11 | "dependencies": {
12 | "react": "^18.2.0",
13 | "react-dom": "^18.2.0"
14 | },
15 | "devDependencies": {
16 | "@types/react": "^18.0.24",
17 | "@types/react-dom": "^18.0.8",
18 | "@vitejs/plugin-react": "^2.2.0",
19 | "typescript": "^4.6.4",
20 | "vite": "^3.2.3"
21 | }
22 | }
--------------------------------------------------------------------------------
/lesson13/public/vite.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/lesson13/src/App.tsx:
--------------------------------------------------------------------------------
1 | import { useState, useEffect, useCallback, useMemo, useRef, MouseEvent, KeyboardEvent } from 'react'
2 |
3 | interface User {
4 | id: number,
5 | username: string,
6 | }
7 |
8 | type fibFunc = (n: number) => number
9 |
10 | const fib: fibFunc = (n) => {
11 | if (n < 2) return n
12 | return fib(n - 1) + fib(n - 2)
13 | }
14 |
15 | const myNum: number = 37
16 |
17 | function App() {
18 | const [count, setCount] = useState(0)
19 | const [users, setUsers] = useState(null)
20 |
21 | const inputRef = useRef(null)
22 |
23 | console.log(inputRef?.current)
24 | console.log(inputRef?.current?.value)
25 |
26 | useEffect(() => {
27 | console.log('mounting')
28 | console.log('Users: ', users)
29 |
30 | return () => console.log('unmounting')
31 | }, [users])
32 |
33 | const addTwo = useCallback((e: MouseEvent | KeyboardEvent): void => setCount(prev => prev + 2),[])
34 |
35 | const result = useMemo(() => fib(myNum),[myNum])
36 |
37 | return (
38 |
39 |
{count}
40 |
41 | {result}
42 |
43 |
44 | )
45 | }
46 |
47 | export default App
48 |
--------------------------------------------------------------------------------
/lesson13/src/assets/react.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/lesson13/src/index.css:
--------------------------------------------------------------------------------
1 | * {
2 | margin: 0;
3 | padding: 0.25rem;
4 | box-sizing: border-box;
5 | }
6 |
7 | body {
8 | background-color: #333;
9 | color: whitesmoke;
10 | display: grid;
11 | place-content: center;
12 | font-size: 150%;
13 | }
14 |
15 | button {
16 | font-size: 2rem;
17 | }
18 |
19 | .bold {
20 | font-weight: bold;
21 | }
22 |
23 | .gold {
24 | color: gold;
25 | }
--------------------------------------------------------------------------------
/lesson13/src/main.tsx:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 | import ReactDOM from 'react-dom/client'
3 | import App from './App'
4 | import './index.css'
5 |
6 | ReactDOM.createRoot(document.getElementById('root') as HTMLElement).render(
7 |
8 |
9 |
10 | )
11 |
--------------------------------------------------------------------------------
/lesson13/src/vite-env.d.ts:
--------------------------------------------------------------------------------
1 | ///
2 |
--------------------------------------------------------------------------------
/lesson13/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "compilerOptions": {
3 | "target": "ESNext",
4 | "useDefineForClassFields": true,
5 | "lib": ["DOM", "DOM.Iterable", "ESNext"],
6 | "allowJs": false,
7 | "skipLibCheck": true,
8 | "esModuleInterop": false,
9 | "allowSyntheticDefaultImports": true,
10 | "strict": true,
11 | "forceConsistentCasingInFileNames": true,
12 | "module": "ESNext",
13 | "moduleResolution": "Node",
14 | "resolveJsonModule": true,
15 | "isolatedModules": true,
16 | "noEmit": true,
17 | "jsx": "react-jsx"
18 | },
19 | "include": ["src"],
20 | "references": [{ "path": "./tsconfig.node.json" }]
21 | }
22 |
--------------------------------------------------------------------------------
/lesson13/tsconfig.node.json:
--------------------------------------------------------------------------------
1 | {
2 | "compilerOptions": {
3 | "composite": true,
4 | "module": "ESNext",
5 | "moduleResolution": "Node",
6 | "allowSyntheticDefaultImports": true
7 | },
8 | "include": ["vite.config.ts"]
9 | }
10 |
--------------------------------------------------------------------------------
/lesson13/vite.config.ts:
--------------------------------------------------------------------------------
1 | import { defineConfig } from 'vite'
2 | import react from '@vitejs/plugin-react'
3 |
4 | // https://vitejs.dev/config/
5 | export default defineConfig({
6 | plugins: [react()]
7 | })
8 |
--------------------------------------------------------------------------------
/lesson14/.gitignore:
--------------------------------------------------------------------------------
1 | # Logs
2 | logs
3 | *.log
4 | npm-debug.log*
5 | yarn-debug.log*
6 | yarn-error.log*
7 | pnpm-debug.log*
8 | lerna-debug.log*
9 |
10 | node_modules
11 | dist
12 | dist-ssr
13 | *.local
14 |
15 | # Editor directories and files
16 | .vscode/*
17 | !.vscode/extensions.json
18 | .idea
19 | .DS_Store
20 | *.suo
21 | *.ntvs*
22 | *.njsproj
23 | *.sln
24 | *.sw?
25 |
--------------------------------------------------------------------------------
/lesson14/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 | Vite + React + TS
8 |
9 |
10 |
11 |
12 |
13 |
14 |
--------------------------------------------------------------------------------
/lesson14/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "lesson14",
3 | "private": true,
4 | "version": "0.0.0",
5 | "type": "module",
6 | "scripts": {
7 | "dev": "vite",
8 | "build": "tsc && vite build",
9 | "preview": "vite preview"
10 | },
11 | "dependencies": {
12 | "react": "^18.2.0",
13 | "react-dom": "^18.2.0"
14 | },
15 | "devDependencies": {
16 | "@types/react": "^18.0.26",
17 | "@types/react-dom": "^18.0.9",
18 | "@vitejs/plugin-react": "^3.0.0",
19 | "typescript": "^4.9.3",
20 | "vite": "^4.0.0"
21 | }
22 | }
--------------------------------------------------------------------------------
/lesson14/public/vite.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/lesson14/src/App.tsx:
--------------------------------------------------------------------------------
1 | import Counter from "./Counter"
2 |
3 | function App() {
4 |
5 | return (
6 | <>
7 | {(num: number) => <>Current Count: {num}>}
8 | >
9 | )
10 | }
11 |
12 | export default App
13 |
--------------------------------------------------------------------------------
/lesson14/src/Counter.tsx:
--------------------------------------------------------------------------------
1 | import { ReactNode, useReducer, ChangeEvent } from 'react'
2 |
3 | const initState = { count: 0, text: '' }
4 |
5 | const enum REDUCER_ACTION_TYPE {
6 | INCREMENT,
7 | DECREMENT,
8 | NEW_INPUT,
9 | }
10 |
11 | type ReducerAction = {
12 | type: REDUCER_ACTION_TYPE,
13 | payload?: string,
14 | }
15 |
16 | const reducer = (state: typeof initState, action: ReducerAction): typeof initState => {
17 | switch (action.type) {
18 | case REDUCER_ACTION_TYPE.INCREMENT:
19 | return { ...state, count: state.count + 1 }
20 | case REDUCER_ACTION_TYPE.DECREMENT:
21 | return { ...state, count: state.count - 1 }
22 | case REDUCER_ACTION_TYPE.NEW_INPUT:
23 | return { ...state, text: action.payload ?? '' }
24 | default:
25 | throw new Error()
26 | }
27 | }
28 |
29 | type ChildrenType = {
30 | children: (num: number) => ReactNode
31 | }
32 |
33 | const Counter = ({ children }: ChildrenType) => {
34 | const [state, dispatch] = useReducer(reducer, initState)
35 |
36 | const increment = () => dispatch({ type: REDUCER_ACTION_TYPE.INCREMENT })
37 | const decrement = () => dispatch({ type: REDUCER_ACTION_TYPE.DECREMENT })
38 | const handleTextInput = (e: ChangeEvent) => {
39 | dispatch({
40 | type: REDUCER_ACTION_TYPE.NEW_INPUT,
41 | payload: e.target.value
42 | })
43 | }
44 |
45 | return (
46 | <>
47 | {children(state.count)}
48 |
49 |
50 |
51 |
52 |
53 | {state.text}
54 | >
55 | )
56 | }
57 | export default Counter
--------------------------------------------------------------------------------
/lesson14/src/assets/react.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/lesson14/src/index.css:
--------------------------------------------------------------------------------
1 | :root {
2 | font-family: Inter, Avenir, Helvetica, Arial, sans-serif;
3 | font-size: 16px;
4 | line-height: 24px;
5 | font-weight: 400;
6 |
7 | color-scheme: light dark;
8 | color: rgba(255, 255, 255, 0.87);
9 | background-color: #242424;
10 |
11 | font-synthesis: none;
12 | text-rendering: optimizeLegibility;
13 | -webkit-font-smoothing: antialiased;
14 | -moz-osx-font-smoothing: grayscale;
15 | -webkit-text-size-adjust: 100%;
16 | }
17 |
18 | a {
19 | font-weight: 500;
20 | color: #646cff;
21 | text-decoration: inherit;
22 | }
23 | a:hover {
24 | color: #535bf2;
25 | }
26 |
27 | body {
28 | margin: auto;
29 | min-width: 100%;
30 | min-height: 100vh;
31 | }
32 |
33 | #root {
34 | display: flex;
35 | flex-flow: column;
36 | justify-content: center;
37 | align-items: center;
38 | }
39 |
40 | h1 {
41 | font-size: 3.2em;
42 | line-height: 1.1;
43 | }
44 |
45 | button {
46 | margin: 0.25em;
47 | border-radius: 8px;
48 | border: 1px solid transparent;
49 | padding: 0.6em 1.2em;
50 | font-size: 1em;
51 | font-weight: 500;
52 | font-family: inherit;
53 | background-color: #1a1a1a;
54 | cursor: pointer;
55 | transition: border-color 0.25s;
56 | }
57 | button:hover {
58 | border-color: #646cff;
59 | }
60 | button:focus,
61 | button:focus-visible {
62 | outline: 4px auto -webkit-focus-ring-color;
63 | }
64 |
65 | @media (prefers-color-scheme: light) {
66 | :root {
67 | color: #213547;
68 | background-color: #ffffff;
69 | }
70 | a:hover {
71 | color: #747bff;
72 | }
73 | button {
74 | background-color: #f9f9f9;
75 | }
76 | }
77 |
--------------------------------------------------------------------------------
/lesson14/src/main.tsx:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 | import ReactDOM from 'react-dom/client'
3 | import App from './App'
4 | import './index.css'
5 |
6 | ReactDOM.createRoot(document.getElementById('root') as HTMLElement).render(
7 |
8 |
9 | ,
10 | )
11 |
--------------------------------------------------------------------------------
/lesson14/src/vite-env.d.ts:
--------------------------------------------------------------------------------
1 | ///
2 |
--------------------------------------------------------------------------------
/lesson14/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "compilerOptions": {
3 | "target": "ESNext",
4 | "useDefineForClassFields": true,
5 | "lib": ["DOM", "DOM.Iterable", "ESNext"],
6 | "allowJs": false,
7 | "skipLibCheck": true,
8 | "esModuleInterop": false,
9 | "allowSyntheticDefaultImports": true,
10 | "strict": true,
11 | "forceConsistentCasingInFileNames": true,
12 | "module": "ESNext",
13 | "moduleResolution": "Node",
14 | "resolveJsonModule": true,
15 | "isolatedModules": true,
16 | "noEmit": true,
17 | "jsx": "react-jsx"
18 | },
19 | "include": ["src"],
20 | "references": [{ "path": "./tsconfig.node.json" }]
21 | }
22 |
--------------------------------------------------------------------------------
/lesson14/tsconfig.node.json:
--------------------------------------------------------------------------------
1 | {
2 | "compilerOptions": {
3 | "composite": true,
4 | "module": "ESNext",
5 | "moduleResolution": "Node",
6 | "allowSyntheticDefaultImports": true
7 | },
8 | "include": ["vite.config.ts"]
9 | }
10 |
--------------------------------------------------------------------------------
/lesson14/vite.config.ts:
--------------------------------------------------------------------------------
1 | import { defineConfig } from 'vite'
2 | import react from '@vitejs/plugin-react'
3 |
4 | // https://vitejs.dev/config/
5 | export default defineConfig({
6 | plugins: [react()],
7 | })
8 |
--------------------------------------------------------------------------------
/lesson15/.gitignore:
--------------------------------------------------------------------------------
1 | # Logs
2 | logs
3 | *.log
4 | npm-debug.log*
5 | yarn-debug.log*
6 | yarn-error.log*
7 | pnpm-debug.log*
8 | lerna-debug.log*
9 |
10 | node_modules
11 | dist
12 | dist-ssr
13 | *.local
14 |
15 | # Editor directories and files
16 | .vscode/*
17 | !.vscode/extensions.json
18 | .idea
19 | .DS_Store
20 | *.suo
21 | *.ntvs*
22 | *.njsproj
23 | *.sln
24 | *.sw?
25 |
--------------------------------------------------------------------------------
/lesson15/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 | Vite + React + TS
8 |
9 |
10 |
11 |
12 |
13 |
14 |
--------------------------------------------------------------------------------
/lesson15/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "lesson14",
3 | "private": true,
4 | "version": "0.0.0",
5 | "type": "module",
6 | "scripts": {
7 | "dev": "vite",
8 | "build": "tsc && vite build",
9 | "preview": "vite preview"
10 | },
11 | "dependencies": {
12 | "react": "^18.2.0",
13 | "react-dom": "^18.2.0"
14 | },
15 | "devDependencies": {
16 | "@types/react": "^18.0.26",
17 | "@types/react-dom": "^18.0.9",
18 | "@vitejs/plugin-react": "^3.0.0",
19 | "typescript": "^4.9.3",
20 | "vite": "^4.0.0"
21 | }
22 | }
--------------------------------------------------------------------------------
/lesson15/public/vite.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/lesson15/src/App.tsx:
--------------------------------------------------------------------------------
1 | import Counter from "./Counter"
2 | import { CounterProvider } from "./context/CounterContext"
3 |
4 | function App() {
5 |
6 | return (
7 | <>
8 |
9 | {(num: number) => <>Current Count: {num}>}
10 |
11 | >
12 | )
13 | }
14 |
15 | export default App
16 |
--------------------------------------------------------------------------------
/lesson15/src/Counter.tsx:
--------------------------------------------------------------------------------
1 | import { ReactNode } from 'react'
2 | import { useCounter } from './context/CounterContext'
3 | import { useCounterText } from './context/CounterContext'
4 |
5 | type ChildrenType = {
6 | children: (num: number) => ReactNode
7 | }
8 |
9 | const Counter = ({ children }: ChildrenType) => {
10 | const { count, increment, decrement } = useCounter()
11 | const { text, handleTextInput } = useCounterText()
12 |
13 | return (
14 | <>
15 | {children(count)}
16 |
17 |
18 |
19 |
20 |
21 | {text}
22 | >
23 | )
24 | }
25 | export default Counter
--------------------------------------------------------------------------------
/lesson15/src/assets/react.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/lesson15/src/context/CounterContext.tsx:
--------------------------------------------------------------------------------
1 | import { createContext, useReducer, ChangeEvent, ReactElement, useCallback, useContext } from "react"
2 |
3 | type StateType = {
4 | count: number;
5 | text: string;
6 | }
7 |
8 | const initState: StateType = { count: 0, text: '' }
9 |
10 | const enum REDUCER_ACTION_TYPE {
11 | INCREMENT,
12 | DECREMENT,
13 | NEW_INPUT,
14 | }
15 |
16 | type ReducerAction = {
17 | type: REDUCER_ACTION_TYPE,
18 | payload?: string,
19 | }
20 |
21 | const reducer = (state: StateType, action: ReducerAction): StateType => {
22 | switch (action.type) {
23 | case REDUCER_ACTION_TYPE.INCREMENT:
24 | return { ...state, count: state.count + 1 }
25 | case REDUCER_ACTION_TYPE.DECREMENT:
26 | return { ...state, count: state.count - 1 }
27 | case REDUCER_ACTION_TYPE.NEW_INPUT:
28 | return { ...state, text: action.payload ?? '' }
29 | default:
30 | throw new Error()
31 | }
32 | }
33 |
34 | const useCounterContext = (initState: StateType) => {
35 | const [state, dispatch] = useReducer(reducer, initState)
36 |
37 | const increment = useCallback(() => dispatch({ type: REDUCER_ACTION_TYPE.INCREMENT }), [])
38 |
39 | const decrement = useCallback(() => dispatch({ type: REDUCER_ACTION_TYPE.DECREMENT }), [])
40 |
41 | const handleTextInput = useCallback((e: ChangeEvent) => {
42 | dispatch({
43 | type: REDUCER_ACTION_TYPE.NEW_INPUT,
44 | payload: e.target.value
45 | })
46 | }, [])
47 |
48 | return { state, increment, decrement, handleTextInput }
49 | }
50 |
51 | type UseCounterContextType = ReturnType
52 |
53 | const initContextState: UseCounterContextType = {
54 | state: initState,
55 | increment: () => { },
56 | decrement: () => { },
57 | handleTextInput: (e: ChangeEvent) => { },
58 | }
59 |
60 | export const CounterContext = createContext(initContextState)
61 |
62 | type ChildrenType = {
63 | children?: ReactElement | ReactElement[] | undefined
64 | }
65 |
66 | export const CounterProvider = ({
67 | children
68 | }: ChildrenType): ReactElement => {
69 | return (
70 |
71 | {children}
72 |
73 | )
74 | }
75 |
76 | type UseCounterHookType = {
77 | count: number,
78 | increment: () => void,
79 | decrement: () => void,
80 | }
81 |
82 | export const useCounter = (): UseCounterHookType => {
83 | const { state: { count }, increment, decrement } = useContext(CounterContext)
84 | return { count, increment, decrement }
85 | }
86 |
87 | type UseCounterTextHookType = {
88 | text: string,
89 | handleTextInput: (e: ChangeEvent) => void,
90 | }
91 |
92 | export const useCounterText = (): UseCounterTextHookType => {
93 | const { state: { text }, handleTextInput } = useContext(CounterContext)
94 | return { text, handleTextInput }
95 | }
--------------------------------------------------------------------------------
/lesson15/src/index.css:
--------------------------------------------------------------------------------
1 | :root {
2 | font-family: Inter, Avenir, Helvetica, Arial, sans-serif;
3 | font-size: 16px;
4 | line-height: 24px;
5 | font-weight: 400;
6 |
7 | color-scheme: light dark;
8 | color: rgba(255, 255, 255, 0.87);
9 | background-color: #242424;
10 |
11 | font-synthesis: none;
12 | text-rendering: optimizeLegibility;
13 | -webkit-font-smoothing: antialiased;
14 | -moz-osx-font-smoothing: grayscale;
15 | -webkit-text-size-adjust: 100%;
16 | }
17 |
18 | a {
19 | font-weight: 500;
20 | color: #646cff;
21 | text-decoration: inherit;
22 | }
23 | a:hover {
24 | color: #535bf2;
25 | }
26 |
27 | body {
28 | margin: auto;
29 | min-width: 100%;
30 | min-height: 100vh;
31 | }
32 |
33 | #root {
34 | display: flex;
35 | flex-flow: column;
36 | justify-content: center;
37 | align-items: center;
38 | }
39 |
40 | h1 {
41 | font-size: 3.2em;
42 | line-height: 1.1;
43 | }
44 |
45 | button {
46 | margin: 0.25em;
47 | border-radius: 8px;
48 | border: 1px solid transparent;
49 | padding: 0.6em 1.2em;
50 | font-size: 1em;
51 | font-weight: 500;
52 | font-family: inherit;
53 | background-color: #1a1a1a;
54 | cursor: pointer;
55 | transition: border-color 0.25s;
56 | }
57 | button:hover {
58 | border-color: #646cff;
59 | }
60 | button:focus,
61 | button:focus-visible {
62 | outline: 4px auto -webkit-focus-ring-color;
63 | }
64 |
65 | @media (prefers-color-scheme: light) {
66 | :root {
67 | color: #213547;
68 | background-color: #ffffff;
69 | }
70 | a:hover {
71 | color: #747bff;
72 | }
73 | button {
74 | background-color: #f9f9f9;
75 | }
76 | }
77 |
--------------------------------------------------------------------------------
/lesson15/src/main.tsx:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 | import ReactDOM from 'react-dom/client'
3 | import App from './App'
4 | import './index.css'
5 |
6 | ReactDOM.createRoot(document.getElementById('root') as HTMLElement).render(
7 |
8 |
9 | ,
10 | )
11 |
--------------------------------------------------------------------------------
/lesson15/src/vite-env.d.ts:
--------------------------------------------------------------------------------
1 | ///
2 |
--------------------------------------------------------------------------------
/lesson15/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "compilerOptions": {
3 | "target": "ESNext",
4 | "useDefineForClassFields": true,
5 | "lib": ["DOM", "DOM.Iterable", "ESNext"],
6 | "allowJs": false,
7 | "skipLibCheck": true,
8 | "esModuleInterop": false,
9 | "allowSyntheticDefaultImports": true,
10 | "strict": true,
11 | "forceConsistentCasingInFileNames": true,
12 | "module": "ESNext",
13 | "moduleResolution": "Node",
14 | "resolveJsonModule": true,
15 | "isolatedModules": true,
16 | "noEmit": true,
17 | "jsx": "react-jsx"
18 | },
19 | "include": ["src"],
20 | "references": [{ "path": "./tsconfig.node.json" }]
21 | }
22 |
--------------------------------------------------------------------------------
/lesson15/tsconfig.node.json:
--------------------------------------------------------------------------------
1 | {
2 | "compilerOptions": {
3 | "composite": true,
4 | "module": "ESNext",
5 | "moduleResolution": "Node",
6 | "allowSyntheticDefaultImports": true
7 | },
8 | "include": ["vite.config.ts"]
9 | }
10 |
--------------------------------------------------------------------------------
/lesson15/vite.config.ts:
--------------------------------------------------------------------------------
1 | import { defineConfig } from 'vite'
2 | import react from '@vitejs/plugin-react'
3 |
4 | // https://vitejs.dev/config/
5 | export default defineConfig({
6 | plugins: [react()],
7 | })
8 |
--------------------------------------------------------------------------------
/lesson16/.gitignore:
--------------------------------------------------------------------------------
1 | # Logs
2 | logs
3 | *.log
4 | npm-debug.log*
5 | yarn-debug.log*
6 | yarn-error.log*
7 | pnpm-debug.log*
8 | lerna-debug.log*
9 |
10 | node_modules
11 | dist
12 | dist-ssr
13 | *.local
14 |
15 | # Editor directories and files
16 | .vscode/*
17 | !.vscode/extensions.json
18 | .idea
19 | .DS_Store
20 | *.suo
21 | *.ntvs*
22 | *.njsproj
23 | *.sln
24 | *.sw?
25 |
--------------------------------------------------------------------------------
/lesson16/data/products.json:
--------------------------------------------------------------------------------
1 | {
2 | "products": [
3 | {
4 | "sku": "item0001",
5 | "name": "Widget",
6 | "price": 9.99
7 | },
8 | {
9 | "sku": "item0002",
10 | "name": "Premium Widget",
11 | "price": 19.99
12 | },
13 | {
14 | "sku": "item0003",
15 | "name": "Deluxe Widget",
16 | "price": 29.99
17 | }
18 | ]
19 | }
--------------------------------------------------------------------------------
/lesson16/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 | Vite + React + TS
8 |
9 |
10 |
11 |
12 |
13 |
14 |
--------------------------------------------------------------------------------
/lesson16/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "lesson16",
3 | "private": true,
4 | "version": "0.0.0",
5 | "type": "module",
6 | "scripts": {
7 | "dev": "vite",
8 | "build": "tsc && vite build",
9 | "preview": "vite preview"
10 | },
11 | "dependencies": {
12 | "react": "^18.2.0",
13 | "react-dom": "^18.2.0"
14 | },
15 | "devDependencies": {
16 | "@types/react": "^18.0.26",
17 | "@types/react-dom": "^18.0.9",
18 | "@vitejs/plugin-react": "^3.0.0",
19 | "typescript": "^4.9.3",
20 | "vite": "^4.0.0"
21 | }
22 | }
--------------------------------------------------------------------------------
/lesson16/public/vite.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/lesson16/src/App.tsx:
--------------------------------------------------------------------------------
1 |
2 |
3 | function App() {
4 |
5 | return (
6 |
7 |
8 |
9 | )
10 | }
11 |
12 | export default App
13 |
--------------------------------------------------------------------------------
/lesson16/src/assets/react.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/lesson16/src/context/CartProvider.tsx:
--------------------------------------------------------------------------------
1 | import { useMemo, useReducer, createContext, ReactElement } from "react"
2 |
3 | export type CartItemType = {
4 | sku: string,
5 | name: string,
6 | price: number,
7 | qty: number,
8 | }
9 |
10 | type CartStateType = { cart: CartItemType[] }
11 |
12 | const initCartState: CartStateType = { cart: [] }
13 |
14 | const REDUCER_ACTION_TYPE = {
15 | ADD: "ADD",
16 | REMOVE: "REMOVE",
17 | QUANTITY: "QUANTITY",
18 | SUBMIT: "SUBMIT",
19 | }
20 |
21 | export type ReducerActionType = typeof REDUCER_ACTION_TYPE
22 |
23 | export type ReducerAction = {
24 | type: string,
25 | payload?: CartItemType,
26 | }
27 |
28 | const reducer = (state: CartStateType, action: ReducerAction): CartStateType => {
29 | switch (action.type) {
30 | case REDUCER_ACTION_TYPE.ADD: {
31 | if (!action.payload) {
32 | throw new Error('action.payload missing in ADD action')
33 | }
34 |
35 | const { sku, name, price } = action.payload
36 |
37 | const filteredCart: CartItemType[] = state.cart.filter(item => item.sku !== sku)
38 |
39 | const itemExists: CartItemType | undefined = state.cart.find(item => item.sku === sku)
40 |
41 | const qty: number = itemExists ? itemExists.qty + 1 : 1
42 |
43 | return { ...state, cart: [...filteredCart, { sku, name, price, qty }] }
44 | }
45 | case REDUCER_ACTION_TYPE.REMOVE: {
46 | if (!action.payload) {
47 | throw new Error('action.payload missing in REMOVE action')
48 | }
49 |
50 | const { sku } = action.payload
51 |
52 | const filteredCart: CartItemType[] = state.cart.filter(item => item.sku !== sku)
53 |
54 | return { ...state, cart: [...filteredCart] }
55 | }
56 | case REDUCER_ACTION_TYPE.QUANTITY: {
57 | if (!action.payload) {
58 | throw new Error('action.payload missing in QUANTITY action')
59 | }
60 |
61 | const { sku, qty } = action.payload
62 |
63 | const itemExists: CartItemType | undefined = state.cart.find(item => item.sku === sku)
64 |
65 | if (!itemExists) {
66 | throw new Error('Item must exist in order to update quantity')
67 | }
68 |
69 | const updatedItem: CartItemType = { ...itemExists, qty }
70 |
71 | const filteredCart: CartItemType[] = state.cart.filter(item => item.sku !== sku)
72 |
73 | return { ...state, cart: [...filteredCart, updatedItem] }
74 | }
75 | case REDUCER_ACTION_TYPE.SUBMIT: {
76 | return { ...state, cart: [] }
77 | }
78 | default:
79 | throw new Error('Unidentified reducer action type')
80 | }
81 | }
82 |
83 | const useCartContext = (initCartState: CartStateType) => {
84 | const [state, dispatch] = useReducer(reducer, initCartState)
85 |
86 | const REDUCER_ACTIONS = useMemo(() => {
87 | return REDUCER_ACTION_TYPE
88 | }, [])
89 |
90 | const totalItems = state.cart.reduce((previousValue, cartItem) => {
91 | return previousValue + cartItem.qty
92 | }, 0)
93 |
94 | const totalPrice = new Intl.NumberFormat('en-US', { style: 'currency', currency: 'USD' }).format(
95 | state.cart.reduce((previousValue, cartItem) => {
96 | return previousValue + (cartItem.qty * cartItem.price)
97 | }, 0)
98 | )
99 |
100 | const cart = state.cart.sort((a, b) => {
101 | const itemA = Number(a.sku.slice(-4))
102 | const itemB = Number(b.sku.slice(-4))
103 | return itemA - itemB
104 | })
105 |
106 | return { dispatch, REDUCER_ACTIONS, totalItems, totalPrice, cart }
107 | }
108 |
109 | export type UseCartContextType = ReturnType
110 |
111 | const initCartContextState: UseCartContextType = {
112 | dispatch: () => { },
113 | REDUCER_ACTIONS: REDUCER_ACTION_TYPE,
114 | totalItems: 0,
115 | totalPrice: '',
116 | cart: [],
117 | }
118 |
119 | const CartContext = createContext(initCartContextState)
120 |
121 | type ChildrenType = { children?: ReactElement | ReactElement[] }
122 |
123 | export const CartProvider = ({ children }: ChildrenType): ReactElement => {
124 | return (
125 |
126 | {children}
127 |
128 | )
129 | }
130 |
131 | export default CartContext
--------------------------------------------------------------------------------
/lesson16/src/context/ProductsProvider.tsx:
--------------------------------------------------------------------------------
1 | import { createContext, ReactElement, useState, useEffect } from "react"
2 |
3 | export type ProductType = {
4 | sku: string,
5 | name: string,
6 | price: number,
7 | }
8 |
9 | const initState: ProductType[] = []
10 | // const initState: ProductType[] = [
11 | // {
12 | // "sku": "item0001",
13 | // "name": "Widget",
14 | // "price": 9.99
15 | // },
16 | // {
17 | // "sku": "item0002",
18 | // "name": "Premium Widget",
19 | // "price": 19.99
20 | // },
21 | // {
22 | // "sku": "item0003",
23 | // "name": "Deluxe Widget",
24 | // "price": 29.99
25 | // }
26 | // ]
27 |
28 | export type UseProductsContextType = { products: ProductType[] }
29 |
30 | const initContextState: UseProductsContextType = { products: [] }
31 |
32 | const ProductsContext = createContext(initContextState)
33 |
34 | type ChildrenType = { children?: ReactElement | ReactElement[] }
35 |
36 | export const ProductsProvider = ({ children }: ChildrenType): ReactElement => {
37 | const [products, setProducts] = useState(initState)
38 |
39 | useEffect(() => {
40 | const fetchProducts = async (): Promise => {
41 | const data = await fetch('http://localhost:3500/products').then(res => {
42 | return res.json()
43 | }).catch(err => {
44 | if (err instanceof Error) console.log(err.message)
45 | })
46 | return data
47 | }
48 |
49 | fetchProducts().then(products => setProducts(products))
50 | }, [])
51 |
52 | return (
53 |
54 | {children}
55 |
56 | )
57 |
58 | }
59 |
60 | export default ProductsContext
--------------------------------------------------------------------------------
/lesson16/src/images/item0001.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/principlesoftware-dev/tutor-typescript/1d3f53073d47126ca510f78ea075cd8f67a42145/lesson16/src/images/item0001.jpg
--------------------------------------------------------------------------------
/lesson16/src/images/item0002.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/principlesoftware-dev/tutor-typescript/1d3f53073d47126ca510f78ea075cd8f67a42145/lesson16/src/images/item0002.jpg
--------------------------------------------------------------------------------
/lesson16/src/images/item0003.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/principlesoftware-dev/tutor-typescript/1d3f53073d47126ca510f78ea075cd8f67a42145/lesson16/src/images/item0003.jpg
--------------------------------------------------------------------------------
/lesson16/src/index.css:
--------------------------------------------------------------------------------
1 | @import url('https://fonts.googleapis.com/css2?family=Nunito&display=swap');
2 |
3 | * {
4 | margin: 0;
5 | padding: 0;
6 | box-sizing: border-box;
7 | }
8 |
9 | .offscreen {
10 | position: absolute;
11 | left: -10000px;
12 | }
13 |
14 | body {
15 | font-family: 'Nunito', sans-serif;
16 | }
17 |
18 | button,
19 | select {
20 | font: inherit;
21 | padding: 0.25em;
22 | }
23 |
24 | img {
25 | display: block;
26 | width: 100%;
27 | height: auto;
28 | border-radius: 10px;
29 | }
30 |
31 | #root {
32 | /* React root div */
33 | min-height: 100vh;
34 | display: flex;
35 | flex-flow: column nowrap;
36 | }
37 |
38 | .main,
39 | .header,
40 | .footer {
41 | padding: 0.25em;
42 | margin: 0 1em;
43 | }
44 |
45 | .main {
46 | display: flex;
47 | gap: 1rem;
48 | }
49 |
50 | .main--products {
51 | flex-flow: row wrap;
52 | justify-content: space-between;
53 | }
54 |
55 | .main--cart {
56 | flex-flow: column nowrap;
57 | }
58 |
59 | .header {
60 | background-color: #fff;
61 | position: sticky;
62 | top: 0;
63 | z-index: 1;
64 | border-bottom: 1px solid #000;
65 | }
66 |
67 | .header__title-bar {
68 | display: flex;
69 | justify-content: space-between;
70 | margin-bottom: 0.5em;
71 | }
72 |
73 | .header__price-box {
74 | text-align: right;
75 | }
76 |
77 | .nav {
78 | display: flex;
79 | justify-content: flex-end;
80 | gap: 0.5em;
81 | }
82 |
83 | .footer {
84 | flex-grow: 1;
85 | display: flex;
86 | flex-flow: column nowrap;
87 | justify-content: flex-end;
88 | }
89 |
90 | .product {
91 | width: 90%;
92 | margin-bottom: 1em;
93 | }
94 |
95 | .product__img {
96 | max-width: 350px;
97 | }
98 |
99 | .cart {
100 | padding: 0;
101 | margin-top: 0.5em;
102 | }
103 |
104 | .cart__item {
105 | display: grid;
106 | grid-template-columns: 4fr 3fr 1fr 1fr;
107 | gap: 0.5rem;
108 | margin-bottom: 0.5em;
109 | }
110 |
111 | .cart__img {
112 | display: none;
113 | min-width: 68px;
114 | }
115 |
116 | .cart__select {
117 | max-height: 48px;
118 | }
119 |
120 | .cart__item-subtotal {
121 | display: none;
122 | text-align: center;
123 | }
124 |
125 | .cart__button {
126 | max-height: 48px;
127 | justify-self: flex-end;
128 | }
129 |
130 | .cart__totals {
131 | display: flex;
132 | flex-flow: column;
133 | gap: 1em;
134 | }
135 |
136 | @media screen and (min-width: 601px) {
137 | .product {
138 | width: 30%;
139 | }
140 |
141 | .cart__item {
142 | grid-template-columns: 3fr 15fr 5fr 1fr 8fr 1fr;
143 | }
144 |
145 | .cart__img, .cart__item-subtotal {
146 | display: block;
147 | }
148 |
149 | .cart__submit {
150 | max-width: 300px;
151 | }
152 | }
--------------------------------------------------------------------------------
/lesson16/src/main.tsx:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 | import ReactDOM from 'react-dom/client'
3 | import App from './App'
4 | import './index.css'
5 |
6 | ReactDOM.createRoot(document.getElementById('root') as HTMLElement).render(
7 |
8 |
9 | ,
10 | )
11 |
--------------------------------------------------------------------------------
/lesson16/src/vite-env.d.ts:
--------------------------------------------------------------------------------
1 | ///
2 |
--------------------------------------------------------------------------------
/lesson16/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "compilerOptions": {
3 | "target": "ESNext",
4 | "useDefineForClassFields": true,
5 | "lib": ["DOM", "DOM.Iterable", "ESNext"],
6 | "allowJs": false,
7 | "skipLibCheck": true,
8 | "esModuleInterop": false,
9 | "allowSyntheticDefaultImports": true,
10 | "strict": true,
11 | "forceConsistentCasingInFileNames": true,
12 | "module": "ESNext",
13 | "moduleResolution": "Node",
14 | "resolveJsonModule": true,
15 | "isolatedModules": true,
16 | "noEmit": true,
17 | "jsx": "react-jsx"
18 | },
19 | "include": ["src"],
20 | "references": [{ "path": "./tsconfig.node.json" }]
21 | }
22 |
--------------------------------------------------------------------------------
/lesson16/tsconfig.node.json:
--------------------------------------------------------------------------------
1 | {
2 | "compilerOptions": {
3 | "composite": true,
4 | "module": "ESNext",
5 | "moduleResolution": "Node",
6 | "allowSyntheticDefaultImports": true
7 | },
8 | "include": ["vite.config.ts"]
9 | }
10 |
--------------------------------------------------------------------------------
/lesson16/vite.config.ts:
--------------------------------------------------------------------------------
1 | import { defineConfig } from 'vite'
2 | import react from '@vitejs/plugin-react'
3 |
4 | // https://vitejs.dev/config/
5 | export default defineConfig({
6 | plugins: [react()],
7 | })
8 |
--------------------------------------------------------------------------------
/lesson17/.gitignore:
--------------------------------------------------------------------------------
1 | # Logs
2 | logs
3 | *.log
4 | npm-debug.log*
5 | yarn-debug.log*
6 | yarn-error.log*
7 | pnpm-debug.log*
8 | lerna-debug.log*
9 |
10 | node_modules
11 | dist
12 | dist-ssr
13 | *.local
14 |
15 | # Editor directories and files
16 | .vscode/*
17 | !.vscode/extensions.json
18 | .idea
19 | .DS_Store
20 | *.suo
21 | *.ntvs*
22 | *.njsproj
23 | *.sln
24 | *.sw?
25 |
--------------------------------------------------------------------------------
/lesson17/data/products.json:
--------------------------------------------------------------------------------
1 | {
2 | "products": [
3 | {
4 | "sku": "item0001",
5 | "name": "Widget",
6 | "price": 9.99
7 | },
8 | {
9 | "sku": "item0002",
10 | "name": "Premium Widget",
11 | "price": 19.99
12 | },
13 | {
14 | "sku": "item0003",
15 | "name": "Deluxe Widget",
16 | "price": 29.99
17 | }
18 | ]
19 | }
--------------------------------------------------------------------------------
/lesson17/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 | Vite + React + TS
8 |
9 |
10 |
11 |
12 |
13 |
14 |
--------------------------------------------------------------------------------
/lesson17/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "lesson17",
3 | "private": true,
4 | "version": "0.0.0",
5 | "type": "module",
6 | "scripts": {
7 | "dev": "vite",
8 | "build": "tsc && vite build",
9 | "preview": "vite preview"
10 | },
11 | "dependencies": {
12 | "react": "^18.2.0",
13 | "react-dom": "^18.2.0"
14 | },
15 | "devDependencies": {
16 | "@types/react": "^18.0.26",
17 | "@types/react-dom": "^18.0.9",
18 | "@vitejs/plugin-react": "^3.0.0",
19 | "typescript": "^4.9.3",
20 | "vite": "^4.0.0"
21 | }
22 | }
--------------------------------------------------------------------------------
/lesson17/public/vite.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/lesson17/src/App.tsx:
--------------------------------------------------------------------------------
1 | import Header from "./components/Header"
2 | import Footer from "./components/Footer"
3 | import Cart from "./components/Cart"
4 | import ProductList from "./components/ProductList"
5 | import { useState } from "react"
6 |
7 | function App() {
8 | const [viewCart, setViewCart] = useState(false)
9 |
10 | const pageContent = viewCart ? :
11 |
12 | const content = (
13 | <>
14 |
15 | {pageContent}
16 |
17 | >
18 | )
19 |
20 | return content
21 | }
22 |
23 | export default App
24 |
--------------------------------------------------------------------------------
/lesson17/src/assets/react.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/lesson17/src/components/Cart.tsx:
--------------------------------------------------------------------------------
1 | import useCart from "../hooks/useCart"
2 | import { useState } from "react"
3 | import CartLineItem from "./CartLineItem"
4 |
5 | const Cart = () => {
6 | const [confirm, setConfirm] = useState(false)
7 | const { dispatch, REDUCER_ACTIONS, totalItems, totalPrice, cart } = useCart()
8 |
9 | const onSubmitOrder = () => {
10 | dispatch({ type: REDUCER_ACTIONS.SUBMIT })
11 | setConfirm(true)
12 | }
13 |
14 | const pageContent = confirm
15 | ? Thank you for your order.
16 | : <>
17 | Cart
18 |
19 | {cart.map(item => {
20 | return (
21 |
27 | )
28 | })}
29 |
30 |
31 |
Total Items: {totalItems}
32 |
Total Price: {totalPrice}
33 |
36 |
37 | >
38 |
39 | const content = (
40 |
41 | {pageContent}
42 |
43 | )
44 |
45 | return content
46 | }
47 | export default Cart
--------------------------------------------------------------------------------
/lesson17/src/components/CartLineItem.tsx:
--------------------------------------------------------------------------------
1 | import { ChangeEvent, ReactElement, memo } from "react"
2 | import { CartItemType } from "../context/CartProvider"
3 | import { ReducerAction } from "../context/CartProvider"
4 | import { ReducerActionType } from "../context/CartProvider"
5 |
6 | type PropsType = {
7 | item: CartItemType,
8 | dispatch: React.Dispatch,
9 | REDUCER_ACTIONS: ReducerActionType,
10 | }
11 |
12 | const CartLineItem = ({ item, dispatch, REDUCER_ACTIONS }: PropsType) => {
13 |
14 | const img: string = new URL(`../images/${item.sku}.jpg`, import.meta.url).href
15 |
16 | const lineTotal: number = (item.qty * item.price)
17 |
18 | const highestQty: number = 20 > item.qty ? 20 : item.qty
19 |
20 | const optionValues: number[] = [...Array(highestQty).keys()].map(i => i + 1)
21 |
22 | const options: ReactElement[] = optionValues.map(val => {
23 | return
24 | })
25 |
26 | const onChangeQty = (e: ChangeEvent) => {
27 | dispatch({
28 | type: REDUCER_ACTIONS.QUANTITY,
29 | payload: { ...item, qty: Number(e.target.value) }
30 | })
31 | }
32 |
33 | const onRemoveFromCart = () => dispatch({
34 | type: REDUCER_ACTIONS.REMOVE,
35 | payload: item,
36 | })
37 |
38 | const content = (
39 |
40 |
41 | {item.name}
42 | {new Intl.NumberFormat('en-US', { style: 'currency', currency: 'USD' }).format(item.price)}
43 |
44 |
47 |
55 |
56 |
57 | {new Intl.NumberFormat('en-US', { style: 'currency', currency: 'USD' }).format(lineTotal)}
58 |
59 |
60 |
68 |
69 | )
70 |
71 | return content
72 | }
73 |
74 | function areItemsEqual({ item: prevItem }: PropsType, { item: nextItem }: PropsType) {
75 | return Object.keys(prevItem).every(key => {
76 | return prevItem[key as keyof CartItemType] === nextItem[key as keyof CartItemType]
77 | })
78 | }
79 |
80 | const MemoizedCartLineItem = memo(CartLineItem, areItemsEqual)
81 |
82 | export default MemoizedCartLineItem
--------------------------------------------------------------------------------
/lesson17/src/components/Footer.tsx:
--------------------------------------------------------------------------------
1 | import useCart from "../hooks/useCart"
2 |
3 | type PropsType = {
4 | viewCart: boolean,
5 | }
6 |
7 | const Footer = ({ viewCart }: PropsType) => {
8 | const { totalItems, totalPrice } = useCart()
9 |
10 | const year: number = new Date().getFullYear()
11 |
12 | const pageContent = viewCart
13 | ? Shopping Cart © {year}
14 | : (
15 | <>
16 | Total Items: {totalItems}
17 | Total Price: {totalPrice}
18 | Shopping Cart © {year}
19 | >
20 | )
21 |
22 | const content = (
23 |
26 | )
27 |
28 | return content
29 | }
30 | export default Footer
--------------------------------------------------------------------------------
/lesson17/src/components/Header.tsx:
--------------------------------------------------------------------------------
1 | import Nav from "./Nav"
2 | import useCart from "../hooks/useCart"
3 |
4 | type PropsType = {
5 | viewCart: boolean,
6 | setViewCart: React.Dispatch>,
7 | }
8 |
9 | const Header = ({ viewCart, setViewCart }: PropsType) => {
10 | const { totalItems, totalPrice } = useCart()
11 |
12 | const content = (
13 |
14 |
15 |
Acme Co.
16 |
17 |
Total Items: {totalItems}
18 |
Total Price: {totalPrice}
19 |
20 |
21 |
22 |
23 | )
24 |
25 | return content
26 | }
27 | export default Header
--------------------------------------------------------------------------------
/lesson17/src/components/Nav.tsx:
--------------------------------------------------------------------------------
1 | type PropsType = {
2 | viewCart: boolean,
3 | setViewCart: React.Dispatch>,
4 | }
5 |
6 | const Nav = ({ viewCart, setViewCart }: PropsType) => {
7 |
8 | const button = viewCart
9 | ?
10 | :
11 |
12 | const content = (
13 |
16 | )
17 |
18 | return content
19 | }
20 | export default Nav
--------------------------------------------------------------------------------
/lesson17/src/components/Product.tsx:
--------------------------------------------------------------------------------
1 | import { ProductType } from "../context/ProductsProvider"
2 | import { ReducerActionType, ReducerAction } from "../context/CartProvider"
3 | import { ReactElement, memo } from "react"
4 |
5 | type PropsType = {
6 | product: ProductType,
7 | dispatch: React.Dispatch,
8 | REDUCER_ACTIONS: ReducerActionType,
9 | inCart: boolean,
10 | }
11 |
12 | const Product = ({ product, dispatch, REDUCER_ACTIONS, inCart }: PropsType): ReactElement => {
13 |
14 | const img: string = new URL(`../images/${product.sku}.jpg`, import.meta.url).href
15 | console.log(img)
16 |
17 | const onAddToCart = () => dispatch({ type: REDUCER_ACTIONS.ADD, payload: { ...product, qty: 1 } })
18 |
19 | const itemInCart = inCart ? ' → Item in Cart: ✔️' : null
20 |
21 | const content =
22 |
23 | {product.name}
24 |
25 | {new Intl.NumberFormat('en-US', { style: 'currency', currency: 'USD' }).format(product.price)}{itemInCart}
26 |
27 |
28 |
29 | return content
30 | }
31 |
32 | function areProductsEqual({ product: prevProduct, inCart: prevInCart }: PropsType, { product: nextProduct, inCart: nextInCart }: PropsType) {
33 | return (
34 | Object.keys(prevProduct).every(key => {
35 | return prevProduct[key as keyof ProductType] ===
36 | nextProduct[key as keyof ProductType]
37 | }) && prevInCart === nextInCart
38 | )
39 | }
40 | const MemoizedProduct = memo(Product, areProductsEqual)
41 |
42 | export default MemoizedProduct
--------------------------------------------------------------------------------
/lesson17/src/components/ProductList.tsx:
--------------------------------------------------------------------------------
1 | import useCart from "../hooks/useCart"
2 | import useProducts from "../hooks/useProducts"
3 | import { UseProductsContextType } from "../context/ProductsProvider"
4 | import { ReactElement } from "react"
5 | import Product from "./Product"
6 |
7 | const ProductList = () => {
8 | const { dispatch, REDUCER_ACTIONS, cart } = useCart()
9 | const { products } = useProducts()
10 |
11 | let pageContent: ReactElement | ReactElement[] = Loading...
12 |
13 | if (products?.length) {
14 | pageContent = products.map(product => {
15 | const inCart: boolean = cart.some(item => item.sku === product.sku)
16 |
17 | return (
18 |
25 | )
26 | })
27 | }
28 |
29 | const content = (
30 |
31 | {pageContent}
32 |
33 | )
34 |
35 | return content
36 | }
37 | export default ProductList
--------------------------------------------------------------------------------
/lesson17/src/context/CartProvider.tsx:
--------------------------------------------------------------------------------
1 | import { useMemo, useReducer, createContext, ReactElement } from "react"
2 |
3 | export type CartItemType = {
4 | sku: string,
5 | name: string,
6 | price: number,
7 | qty: number,
8 | }
9 |
10 | type CartStateType = { cart: CartItemType[] }
11 |
12 | const initCartState: CartStateType = { cart: [] }
13 |
14 | const REDUCER_ACTION_TYPE = {
15 | ADD: "ADD",
16 | REMOVE: "REMOVE",
17 | QUANTITY: "QUANTITY",
18 | SUBMIT: "SUBMIT",
19 | }
20 |
21 | export type ReducerActionType = typeof REDUCER_ACTION_TYPE
22 |
23 | export type ReducerAction = {
24 | type: string,
25 | payload?: CartItemType,
26 | }
27 |
28 | const reducer = (state: CartStateType, action: ReducerAction): CartStateType => {
29 | switch (action.type) {
30 | case REDUCER_ACTION_TYPE.ADD: {
31 | if (!action.payload) {
32 | throw new Error('action.payload missing in ADD action')
33 | }
34 |
35 | const { sku, name, price } = action.payload
36 |
37 | const filteredCart: CartItemType[] = state.cart.filter(item => item.sku !== sku)
38 |
39 | const itemExists: CartItemType | undefined = state.cart.find(item => item.sku === sku)
40 |
41 | const qty: number = itemExists ? itemExists.qty + 1 : 1
42 |
43 | return { ...state, cart: [...filteredCart, { sku, name, price, qty }] }
44 | }
45 | case REDUCER_ACTION_TYPE.REMOVE: {
46 | if (!action.payload) {
47 | throw new Error('action.payload missing in REMOVE action')
48 | }
49 |
50 | const { sku } = action.payload
51 |
52 | const filteredCart: CartItemType[] = state.cart.filter(item => item.sku !== sku)
53 |
54 | return { ...state, cart: [...filteredCart] }
55 | }
56 | case REDUCER_ACTION_TYPE.QUANTITY: {
57 | if (!action.payload) {
58 | throw new Error('action.payload missing in QUANTITY action')
59 | }
60 |
61 | const { sku, qty } = action.payload
62 |
63 | const itemExists: CartItemType | undefined = state.cart.find(item => item.sku === sku)
64 |
65 | if (!itemExists) {
66 | throw new Error('Item must exist in order to update quantity')
67 | }
68 |
69 | const updatedItem: CartItemType = { ...itemExists, qty }
70 |
71 | const filteredCart: CartItemType[] = state.cart.filter(item => item.sku !== sku)
72 |
73 | return { ...state, cart: [...filteredCart, updatedItem] }
74 | }
75 | case REDUCER_ACTION_TYPE.SUBMIT: {
76 | return { ...state, cart: [] }
77 | }
78 | default:
79 | throw new Error('Unidentified reducer action type')
80 | }
81 | }
82 |
83 | const useCartContext = (initCartState: CartStateType) => {
84 | const [state, dispatch] = useReducer(reducer, initCartState)
85 |
86 | const REDUCER_ACTIONS = useMemo(() => {
87 | return REDUCER_ACTION_TYPE
88 | }, [])
89 |
90 | const totalItems = state.cart.reduce((previousValue, cartItem) => {
91 | return previousValue + cartItem.qty
92 | }, 0)
93 |
94 | const totalPrice = new Intl.NumberFormat('en-US', { style: 'currency', currency: 'USD' }).format(
95 | state.cart.reduce((previousValue, cartItem) => {
96 | return previousValue + (cartItem.qty * cartItem.price)
97 | }, 0)
98 | )
99 |
100 | const cart = state.cart.sort((a, b) => {
101 | const itemA = Number(a.sku.slice(-4))
102 | const itemB = Number(b.sku.slice(-4))
103 | return itemA - itemB
104 | })
105 |
106 | return { dispatch, REDUCER_ACTIONS, totalItems, totalPrice, cart }
107 | }
108 |
109 | export type UseCartContextType = ReturnType
110 |
111 | const initCartContextState: UseCartContextType = {
112 | dispatch: () => { },
113 | REDUCER_ACTIONS: REDUCER_ACTION_TYPE,
114 | totalItems: 0,
115 | totalPrice: '',
116 | cart: [],
117 | }
118 |
119 | const CartContext = createContext(initCartContextState)
120 |
121 | type ChildrenType = { children?: ReactElement | ReactElement[] }
122 |
123 | export const CartProvider = ({ children }: ChildrenType): ReactElement => {
124 | return (
125 |
126 | {children}
127 |
128 | )
129 | }
130 |
131 | export default CartContext
--------------------------------------------------------------------------------
/lesson17/src/context/ProductsProvider.tsx:
--------------------------------------------------------------------------------
1 | import { createContext, ReactElement, useState, useEffect } from "react"
2 |
3 | export type ProductType = {
4 | sku: string,
5 | name: string,
6 | price: number,
7 | }
8 |
9 | //const initState: ProductType[] = []
10 | const initState: ProductType[] = [
11 | {
12 | "sku": "item0001",
13 | "name": "Widget",
14 | "price": 9.99
15 | },
16 | {
17 | "sku": "item0002",
18 | "name": "Premium Widget",
19 | "price": 19.99
20 | },
21 | {
22 | "sku": "item0003",
23 | "name": "Deluxe Widget",
24 | "price": 29.99
25 | }
26 | ]
27 |
28 | export type UseProductsContextType = { products: ProductType[] }
29 |
30 | const initContextState: UseProductsContextType = { products: [] }
31 |
32 | const ProductsContext = createContext(initContextState)
33 |
34 | type ChildrenType = { children?: ReactElement | ReactElement[] }
35 |
36 | export const ProductsProvider = ({ children }: ChildrenType): ReactElement => {
37 | const [products, setProducts] = useState(initState)
38 |
39 | // useEffect(() => {
40 | // const fetchProducts = async (): Promise => {
41 | // const data = await fetch('http://localhost:3500/products').then(res => {
42 | // return res.json()
43 | // }).catch(err => {
44 | // if (err instanceof Error) console.log(err.message)
45 | // })
46 | // return data
47 | // }
48 |
49 | // fetchProducts().then(products => setProducts(products))
50 | // }, [])
51 |
52 | return (
53 |
54 | {children}
55 |
56 | )
57 |
58 | }
59 |
60 | export default ProductsContext
--------------------------------------------------------------------------------
/lesson17/src/hooks/useCart.tsx:
--------------------------------------------------------------------------------
1 | import { useContext } from "react"
2 | import CartContext from "../context/CartProvider"
3 | import { UseCartContextType } from "../context/CartProvider"
4 |
5 | const useCart = (): UseCartContextType => {
6 | return useContext(CartContext)
7 | }
8 |
9 | export default useCart
--------------------------------------------------------------------------------
/lesson17/src/hooks/useProducts.tsx:
--------------------------------------------------------------------------------
1 | import { useContext } from "react"
2 | import ProductsContext from "../context/ProductsProvider"
3 | import { UseProductsContextType } from "../context/ProductsProvider"
4 |
5 | const useProducts = (): UseProductsContextType => {
6 | return useContext(ProductsContext)
7 | }
8 |
9 | export default useProducts
--------------------------------------------------------------------------------
/lesson17/src/images/item0001.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/principlesoftware-dev/tutor-typescript/1d3f53073d47126ca510f78ea075cd8f67a42145/lesson17/src/images/item0001.jpg
--------------------------------------------------------------------------------
/lesson17/src/images/item0002.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/principlesoftware-dev/tutor-typescript/1d3f53073d47126ca510f78ea075cd8f67a42145/lesson17/src/images/item0002.jpg
--------------------------------------------------------------------------------
/lesson17/src/images/item0003.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/principlesoftware-dev/tutor-typescript/1d3f53073d47126ca510f78ea075cd8f67a42145/lesson17/src/images/item0003.jpg
--------------------------------------------------------------------------------
/lesson17/src/index.css:
--------------------------------------------------------------------------------
1 | @import url('https://fonts.googleapis.com/css2?family=Nunito&display=swap');
2 |
3 | * {
4 | margin: 0;
5 | padding: 0;
6 | box-sizing: border-box;
7 | }
8 |
9 | .offscreen {
10 | position: absolute;
11 | left: -10000px;
12 | }
13 |
14 | body {
15 | font-family: 'Nunito', sans-serif;
16 | }
17 |
18 | button,
19 | select {
20 | font: inherit;
21 | padding: 0.25em;
22 | }
23 |
24 | img {
25 | display: block;
26 | width: 100%;
27 | height: auto;
28 | border-radius: 10px;
29 | }
30 |
31 | #root {
32 | /* React root div */
33 | min-height: 100vh;
34 | display: flex;
35 | flex-flow: column nowrap;
36 | }
37 |
38 | .main,
39 | .header,
40 | .footer {
41 | padding: 0.25em;
42 | margin: 0 1em;
43 | }
44 |
45 | .main {
46 | display: flex;
47 | gap: 1rem;
48 | }
49 |
50 | .main--products {
51 | flex-flow: row wrap;
52 | justify-content: space-between;
53 | }
54 |
55 | .main--cart {
56 | flex-flow: column nowrap;
57 | }
58 |
59 | .header {
60 | background-color: #fff;
61 | position: sticky;
62 | top: 0;
63 | z-index: 1;
64 | border-bottom: 1px solid #000;
65 | }
66 |
67 | .header__title-bar {
68 | display: flex;
69 | justify-content: space-between;
70 | margin-bottom: 0.5em;
71 | }
72 |
73 | .header__price-box {
74 | text-align: right;
75 | }
76 |
77 | .nav {
78 | display: flex;
79 | justify-content: flex-end;
80 | gap: 0.5em;
81 | }
82 |
83 | .footer {
84 | flex-grow: 1;
85 | display: flex;
86 | flex-flow: column nowrap;
87 | justify-content: flex-end;
88 | }
89 |
90 | .product {
91 | width: 90%;
92 | margin-bottom: 1em;
93 | }
94 |
95 | .product__img {
96 | max-width: 350px;
97 | }
98 |
99 | .cart {
100 | padding: 0;
101 | margin-top: 0.5em;
102 | }
103 |
104 | .cart__item {
105 | display: grid;
106 | grid-template-columns: 4fr 3fr 1fr 1fr;
107 | gap: 0.5rem;
108 | margin-bottom: 0.5em;
109 | }
110 |
111 | .cart__img {
112 | display: none;
113 | min-width: 68px;
114 | }
115 |
116 | .cart__select {
117 | max-height: 48px;
118 | }
119 |
120 | .cart__item-subtotal {
121 | display: none;
122 | text-align: center;
123 | }
124 |
125 | .cart__button {
126 | max-height: 48px;
127 | justify-self: flex-end;
128 | }
129 |
130 | .cart__totals {
131 | display: flex;
132 | flex-flow: column;
133 | gap: 1em;
134 | }
135 |
136 | @media screen and (min-width: 601px) {
137 | .product {
138 | width: 30%;
139 | }
140 |
141 | .cart__item {
142 | grid-template-columns: 3fr 15fr 5fr 1fr 8fr 1fr;
143 | }
144 |
145 | .cart__img, .cart__item-subtotal {
146 | display: block;
147 | }
148 |
149 | .cart__submit {
150 | max-width: 300px;
151 | }
152 | }
--------------------------------------------------------------------------------
/lesson17/src/main.tsx:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 | import ReactDOM from 'react-dom/client'
3 | import App from './App'
4 | import './index.css'
5 |
6 | import { CartProvider } from './context/CartProvider'
7 | import { ProductsProvider } from './context/ProductsProvider'
8 |
9 | ReactDOM.createRoot(document.getElementById('root') as HTMLElement).render(
10 |
11 |
12 |
13 |
14 |
15 |
16 | ,
17 | )
18 |
--------------------------------------------------------------------------------
/lesson17/src/vite-env.d.ts:
--------------------------------------------------------------------------------
1 | ///
2 |
--------------------------------------------------------------------------------
/lesson17/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "compilerOptions": {
3 | "target": "ESNext",
4 | "useDefineForClassFields": true,
5 | "lib": ["DOM", "DOM.Iterable", "ESNext"],
6 | "allowJs": false,
7 | "skipLibCheck": true,
8 | "esModuleInterop": false,
9 | "allowSyntheticDefaultImports": true,
10 | "strict": true,
11 | "forceConsistentCasingInFileNames": true,
12 | "module": "ESNext",
13 | "moduleResolution": "Node",
14 | "resolveJsonModule": true,
15 | "isolatedModules": true,
16 | "noEmit": true,
17 | "jsx": "react-jsx"
18 | },
19 | "include": ["src"],
20 | "references": [{ "path": "./tsconfig.node.json" }]
21 | }
22 |
--------------------------------------------------------------------------------
/lesson17/tsconfig.node.json:
--------------------------------------------------------------------------------
1 | {
2 | "compilerOptions": {
3 | "composite": true,
4 | "module": "ESNext",
5 | "moduleResolution": "Node",
6 | "allowSyntheticDefaultImports": true
7 | },
8 | "include": ["vite.config.ts"]
9 | }
10 |
--------------------------------------------------------------------------------
/lesson17/vite.config.ts:
--------------------------------------------------------------------------------
1 | import { defineConfig } from 'vite'
2 | import react from '@vitejs/plugin-react'
3 |
4 | // https://vitejs.dev/config/
5 | export default defineConfig({
6 | plugins: [react()],
7 | })
8 |
--------------------------------------------------------------------------------