Skip to content

Commit e643640

Browse files
committed
add extra practice
1 parent a8da1ef commit e643640

23 files changed

Lines changed: 1745 additions & 3 deletions
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
# Practice Everything
2+
3+
🐨 Open <InlineFile file="index.ts" /> and work through each section.
Lines changed: 171 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,171 @@
1+
// Comprehensive OOP Practice
2+
// Work through each section, implementing the classes and functions as instructed.
3+
4+
// ============================================================================
5+
// SECTION 1: Class Basics
6+
// ============================================================================
7+
8+
// 🐨 Create a class `Product` with:
9+
// - public fields: name (string), price (number)
10+
// - a constructor that takes name and price
11+
// - a method `getDescription()` that returns "{name}: ${price}"
12+
13+
// 🐨 Create a class `ShoppingCart` with:
14+
// - a public field `items` initialized to an empty array of Product
15+
// - a method `addItem(item: Product)` that adds to items
16+
// - a method `getTotal()` that returns the total price of all items
17+
18+
// Test Section 1:
19+
// const cart = new ShoppingCart()
20+
// cart.addItem(new Product('Mug', 12))
21+
// cart.addItem(new Product('Notebook', 8))
22+
// console.log(cart.getTotal()) // 20
23+
24+
// ============================================================================
25+
// SECTION 2: Private Fields & Defaults
26+
// ============================================================================
27+
28+
// 🐨 Create a class `BankAccount` with:
29+
// - a private field `#balance` (number)
30+
// - a constructor that takes an initial balance (default 0)
31+
// - a method `deposit(amount: number)` that adds to balance
32+
// - a method `withdraw(amount: number)` that subtracts from balance
33+
// - a method `getBalance()` that returns the balance
34+
35+
// 🐨 Create a class `Config` with:
36+
// - public fields: host (string), port (number), role (string)
37+
// - default values: host = 'localhost', port = 3000, role = 'user'
38+
39+
// Test Section 2:
40+
// const account = new BankAccount()
41+
// account.deposit(50)
42+
// account.withdraw(10)
43+
// console.log(account.getBalance()) // 40
44+
// const config = new Config()
45+
// console.log(config.host, config.port, config.role)
46+
47+
// ============================================================================
48+
// SECTION 3: Interfaces & Implementations
49+
// ============================================================================
50+
51+
// 🐨 Create an interface `PaymentMethod` with:
52+
// - pay(amount: number): string
53+
54+
// 🐨 Create a class `CreditCard` that implements PaymentMethod
55+
// - public field: cardNumber (string)
56+
// - pay returns "Paid $${amount} with card ${cardNumber}"
57+
58+
// 🐨 Create a class `PayPal` that implements PaymentMethod
59+
// - public field: email (string)
60+
// - pay returns "Paid $${amount} with PayPal ${email}"
61+
62+
// Test Section 3:
63+
// const card = new CreditCard('1234')
64+
// const paypal = new PayPal('user@example.com')
65+
// console.log(card.pay(25))
66+
// console.log(paypal.pay(25))
67+
68+
// ============================================================================
69+
// SECTION 4: Programming to Abstractions
70+
// ============================================================================
71+
72+
// 🐨 Create a function `processPayment` that:
73+
// - takes a PaymentMethod and an amount
74+
// - returns the result of calling pay on the method
75+
76+
// 🐨 Create a class `GiftCard` that implements PaymentMethod
77+
// - public field: code (string)
78+
// - pay returns "Paid $${amount} with gift card ${code}"
79+
80+
// Test Section 4:
81+
// console.log(processPayment(card, 40))
82+
// console.log(processPayment(new GiftCard('GC-001'), 40))
83+
84+
// ============================================================================
85+
// SECTION 5: Inheritance
86+
// ============================================================================
87+
88+
// 🐨 Create a base class `Package` with:
89+
// - public fields: label (string), weight (number)
90+
// - a method `getLabel()` that returns "{label} ({weight}kg)"
91+
92+
// 🐨 Create a class `Box` that extends Package
93+
// - add a field `width` (number)
94+
// - call super in the constructor
95+
96+
// 🐨 Create a class `Crate` that extends Package
97+
// - add a field `material` (string)
98+
// - call super in the constructor
99+
100+
// Test Section 5:
101+
// const box = new Box('Box A', 5, 10)
102+
// const crate = new Crate('Crate B', 20, 'wood')
103+
// console.log(box.getLabel())
104+
// console.log(crate.getLabel())
105+
106+
// ============================================================================
107+
// SECTION 6: Method Overriding
108+
// ============================================================================
109+
110+
// 🐨 Create a class `Shape` with:
111+
// - a method `getArea()` that returns 0
112+
113+
// 🐨 Create a class `Circle` that extends Shape
114+
// - field: radius (number)
115+
// - override getArea to return Math.PI * radius * radius
116+
117+
// 🐨 Create a class `Rectangle` that extends Shape
118+
// - fields: width (number), height (number)
119+
// - override getArea to return width * height
120+
121+
// Test Section 6:
122+
// console.log(new Circle(2).getArea())
123+
// console.log(new Rectangle(3, 4).getArea())
124+
125+
// ============================================================================
126+
// SECTION 7: Substitutability
127+
// ============================================================================
128+
129+
// 🐨 Create a class `MediaFile` with:
130+
// - a public field `filename` (string)
131+
// - a method `play()` that returns "Playing {filename}"
132+
133+
// 🐨 Create a class `AudioFile` that extends MediaFile
134+
// - override play to return "Playing audio {filename}"
135+
136+
// 🐨 Create a class `VideoFile` that extends MediaFile
137+
// - override play to return "Playing video {filename}"
138+
139+
// 🐨 Create a class `MediaPlayer` with:
140+
// - a method `playFile(file: MediaFile)` that returns file.play()
141+
142+
// Test Section 7:
143+
// const player = new MediaPlayer()
144+
// console.log(player.playFile(new AudioFile('song.mp3')))
145+
// console.log(player.playFile(new VideoFile('movie.mp4')))
146+
147+
// ============================================================================
148+
// SECTION 8: Composition & Dependency Injection
149+
// ============================================================================
150+
151+
// 🐨 Create a class `Logger` with:
152+
// - a method `log(message: string)` that returns `Log: {message}`
153+
154+
// 🐨 Create a class `ConsoleLogger` that extends Logger
155+
// - override log to call console.log and return the same string
156+
157+
// 🐨 Create a class `InMemoryLogger` that extends Logger
158+
// - a private field `#logs` (array of strings)
159+
// - override log to store messages in #logs
160+
// - a method `getLogs()` that returns a copy of #logs
161+
162+
// 🐨 Create a class `ReportService` that takes a Logger in its constructor
163+
// - method `generateReport(title: string)` that logs "Report: {title}"
164+
165+
// Test Section 8:
166+
// const logger = new InMemoryLogger()
167+
// const service = new ReportService(logger)
168+
// service.generateReport('Weekly Summary')
169+
// console.log(logger.getLogs())
170+
171+
export {}
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
{
2+
"name": "extra_01.practice-everything",
3+
"type": "module",
4+
"scripts": {
5+
"start": "npx @kentcdodds/log-module@latest ./index.ts"
6+
}
7+
}
Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
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?
Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
# Inventory Manager
2+
3+
👨‍💼 Welcome to the Inventory Manager! This is a practical React app that uses
4+
TypeScript classes for the core business logic. You'll implement the classes
5+
in a utilities module to power the UI.
6+
7+
## Getting Started
8+
9+
🐨 Open <InlineFile file="src/classes.ts" /> and implement the classes,
10+
interfaces, and methods. Every 🐨 comment is something for you to fill in.
11+
12+
🐨 Open <InlineFile file="src/app.tsx" /> and look for TODO callouts in the UI.
13+
As you implement the class logic, those TODOs will turn into real data.
14+
15+
## Running the App
16+
17+
```sh
18+
npm run dev
19+
```
20+
21+
## What to Implement
22+
23+
### In `src/classes.ts`
24+
25+
1. Inventory base class with private fields
26+
2. Interfaces for Sellable and Trackable behaviors
27+
3. Inheritance for Electronics, Clothing, and Perishable items
28+
4. Method overriding for descriptions and pricing
29+
5. Composition with logger implementations and InventoryManager
30+
31+
### In `src/app.tsx`
32+
33+
1. Verify the UI updates once class methods are implemented
34+
2. Check the TODO callouts to confirm you've completed each piece
35+
36+
## Tips
37+
38+
💰 Start with the base `InventoryItem` class. Many other classes depend on it.
39+
40+
💰 When you implement pricing, be consistent about using `basePrice`,
41+
`quantity`, and `discountPercent`.
42+
43+
💰 Use the logger classes to see the effects of dependency injection.
44+
45+
## No Tests, No Solutions
46+
47+
📝 This is practice time! There are no tests or solutions. Experiment, make
48+
mistakes, and build confidence by applying what you've learned.
Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
import js from '@eslint/js'
2+
import globals from 'globals'
3+
import reactHooks from 'eslint-plugin-react-hooks'
4+
import reactRefresh from 'eslint-plugin-react-refresh'
5+
import tseslint from 'typescript-eslint'
6+
import { defineConfig, globalIgnores } from 'eslint/config'
7+
8+
export default defineConfig([
9+
globalIgnores(['dist']),
10+
{
11+
files: ['**/*.{ts,tsx}'],
12+
extends: [
13+
js.configs.recommended,
14+
tseslint.configs.recommended,
15+
reactHooks.configs.flat.recommended,
16+
reactRefresh.configs.vite,
17+
],
18+
languageOptions: {
19+
ecmaVersion: 2020,
20+
globals: globals.browser,
21+
},
22+
rules: {
23+
'@typescript-eslint/no-unused-vars': 'off',
24+
},
25+
},
26+
])
Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
<!doctype html>
2+
<html lang="en">
3+
<head>
4+
<meta charset="UTF-8" />
5+
<link rel="icon" type="image/svg+xml" href="/vite.svg" />
6+
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
7+
<title>02.inventory-manager</title>
8+
</head>
9+
<body>
10+
<div id="root"></div>
11+
<script type="module" src="/src/main.tsx"></script>
12+
</body>
13+
</html>
Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
{
2+
"name": "extra_02.inventory-manager",
3+
"private": true,
4+
"version": "0.0.0",
5+
"type": "module",
6+
"scripts": {
7+
"dev": "vite",
8+
"build": "tsc -b && vite build",
9+
"lint": "eslint .",
10+
"preview": "vite preview"
11+
},
12+
"dependencies": {
13+
"react": "^19.2.0",
14+
"react-dom": "^19.2.0"
15+
},
16+
"devDependencies": {
17+
"@eslint/js": "^9.39.1",
18+
"@types/node": "^24.10.1",
19+
"@types/react": "^19.2.5",
20+
"@types/react-dom": "^19.2.3",
21+
"@vitejs/plugin-react": "^5.1.1",
22+
"eslint": "^9.39.1",
23+
"eslint-plugin-react-hooks": "^7.0.1",
24+
"eslint-plugin-react-refresh": "^0.4.24",
25+
"globals": "^16.5.0",
26+
"typescript": "~5.9.3",
27+
"typescript-eslint": "^8.46.4",
28+
"vite": "^7.2.4"
29+
}
30+
}
Lines changed: 1 addition & 0 deletions
Loading

0 commit comments

Comments
 (0)