Skip to content

Commit 31e5f2a

Browse files
committed
Add TypeScript advanced track
1 parent 4ec217f commit 31e5f2a

33 files changed

Lines changed: 1137 additions & 33 deletions

File tree

LANGUAGE_PARITY_MATRIX.md

Lines changed: 9 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ This matrix tracks module and checkpoint parity across C++, C#, Go, Python, and
44

55
- Canonical order is defined by the C++ track.
66
- C++, C#, Go, and Python now reach module and checkpoint parity through `04-expert`.
7-
- TypeScript is the newest incremental track with `01-foundations` and `02-core` implemented first.
7+
- TypeScript is the newest incremental track with `01-foundations`, `02-core`, and `03-advanced` implemented first.
88
- Status labels:
99
- `Done`: module implemented with example, exercises, and README.
1010
- `Planned`: module not implemented yet, already queued in order.
@@ -47,17 +47,17 @@ Current parity progress in non-C++ tracks:
4747
- C#: `5/5` modules complete in `03-advanced`, `5/5` in `04-expert`
4848
- Go: `5/5` modules complete in `03-advanced`, `5/5` in `04-expert`
4949
- Python: `5/5` modules complete in `03-advanced`, `5/5` in `04-expert`
50-
- TypeScript: `03-advanced` and `04-expert` planned after foundations and core
50+
- TypeScript: `5/5` modules complete in `03-advanced`; `04-expert` planned next
5151

5252
### Advanced (`03-advanced`) - Current Expansion Queue
5353

5454
| Order | Module | C++ | C# | Go | Python | TypeScript |
5555
| --- | --- | --- | --- | --- | --- | --- |
56-
| 1 | structs-and-classes | Done | Done | Done | Done | Planned |
57-
| 2 | constructors-and-invariants | Done | Done | Done | Done | Planned |
58-
| 3 | copy-and-move-semantics | Done | Done | Done | Done | Planned |
59-
| 4 | inheritance-and-polymorphism | Done | Done | Done | Done | Planned |
60-
| 5 | templates-basics | Done | Done | Done | Done | Planned |
56+
| 1 | structs-and-classes | Done | Done | Done | Done | Done |
57+
| 2 | constructors-and-invariants | Done | Done | Done | Done | Done |
58+
| 3 | copy-and-move-semantics | Done | Done | Done | Done | Done |
59+
| 4 | inheritance-and-polymorphism | Done | Done | Done | Done | Done |
60+
| 5 | templates-basics | Done | Done | Done | Done | Done |
6161

6262
`04-expert` projects and assessments are now implemented across C#, Go, and Python.
6363

@@ -84,7 +84,7 @@ Status labels:
8484
| --- | --- | --- | --- | --- | --- |
8585
| 01-foundations | Done | Done | Done | Done | Done |
8686
| 02-core | Done | Done | Done | Done | Done |
87-
| 03-advanced | Done | Done | Done | Done | Planned |
87+
| 03-advanced | Done | Done | Done | Done | Done |
8888
| 04-expert | Done | Done | Done | Done | Planned |
8989

9090
### Assessments
@@ -93,5 +93,5 @@ Status labels:
9393
| --- | --- | --- | --- | --- | --- |
9494
| 01-foundations | Done | Done | Done | Done | Done |
9595
| 02-core | Done | Done | Done | Done | Done |
96-
| 03-advanced | Done | Done | Done | Done | Planned |
96+
| 03-advanced | Done | Done | Done | Done | Done |
9797
| 04-expert | Done | Done | Done | Done | Planned |

README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -54,7 +54,7 @@ If you only want to learn one track, follow that track README first because lang
5454
| C# | 01-foundations, 02-core, 03-advanced, 04-expert | 8/8 foundations modules, 6/6 core modules, 5/5 advanced modules, 5/5 expert modules, 4/4 projects, 4/4 assessments | Module and checkpoint parity complete through expert |
5555
| Go | 01-foundations, 02-core, 03-advanced, 04-expert | 8/8 foundations modules, 6/6 core modules, 5/5 advanced modules, 5/5 expert modules, 4/4 projects, 4/4 assessments | Module and checkpoint parity complete through expert |
5656
| Python | 01-foundations, 02-core, 03-advanced, 04-expert | 8/8 foundations modules, 6/6 core modules, 5/5 advanced modules, 5/5 expert modules, 4/4 projects, 4/4 assessments | Module and checkpoint parity complete through expert |
57-
| TypeScript | 01-foundations, 02-core | 8/8 foundations modules, 6/6 core modules, 2/4 projects, 2/4 assessments | Foundations and core complete; advanced and above planned |
57+
| TypeScript | 01-foundations, 02-core, 03-advanced | 8/8 foundations modules, 6/6 core modules, 5/5 advanced modules, 3/4 projects, 3/4 assessments | Foundations, core, and advanced complete; expert planned |
5858

5959
Parity planning reference: [LANGUAGE_PARITY_MATRIX.md](LANGUAGE_PARITY_MATRIX.md)
6060

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
# TypeScript 03 Advanced
2+
3+
This level moves the TypeScript track into object modeling, invariants, reusable abstractions, and generics.
4+
5+
## Learning Metadata
6+
7+
- Difficulty: Advanced.
8+
- Estimated Time: 4-6 hours across modules, project, and assessment.
9+
- Prerequisites: Completion of `01-foundations` and `02-core`, including at least one checkpoint per level.
10+
- Study Strategy: Trace object state carefully, compare reference behavior with earlier array examples, and leave generics for last so they generalize concepts you already trust.
11+
12+
## Module Order
13+
14+
1. [structs-and-classes](./structs-and-classes/README.md)
15+
2. [constructors-and-invariants](./constructors-and-invariants/README.md)
16+
3. [copy-and-move-semantics](./copy-and-move-semantics/README.md)
17+
4. [inheritance-and-polymorphism](./inheritance-and-polymorphism/README.md)
18+
5. [templates-basics](./templates-basics/README.md)
19+
20+
Track progress in [../CHECKLIST.md](../CHECKLIST.md).
21+
22+
## Study Tip
23+
24+
Use `templates-basics` last so the generic helpers feel like a cleanup of earlier advanced modules instead of a brand-new topic.
Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,55 @@
1+
# Constructors and Invariants
2+
3+
This module focuses on constructors that reject invalid state and keep objects trustworthy.
4+
5+
## Learning Metadata
6+
7+
- Difficulty: Advanced.
8+
- Estimated Time: 30-45 minutes.
9+
- Prerequisites: `03-advanced/structs-and-classes` and `02-core/input-validation`.
10+
- Cross-Language Lens: Compare constructor validation across C++, C#, Go factory patterns, Python initializers, and TypeScript classes.
11+
12+
## Quick Run
13+
14+
~~~bash
15+
npm run build:typescript
16+
node build/typescript/03-advanced/constructors-and-invariants/example/main.js
17+
~~~
18+
19+
## Topics Covered
20+
21+
- Constructor guards.
22+
- Private mutable state with public methods.
23+
- Preventing impossible states.
24+
- Keeping mutation rules near the data they protect.
25+
26+
## Common Pitfalls
27+
28+
- Allowing invalid values into the constructor and hoping later methods fix them.
29+
- Exposing fields that bypass invariant checks.
30+
- Returning partially initialized objects.
31+
32+
## Exercise Focus
33+
34+
- exercises/01.ts: build a validated bank account.
35+
- exercises/02.ts: build a temperature range that preserves `minimum <= maximum`.
36+
37+
### Exercise Specs
38+
39+
1. exercises/01.ts
40+
- Input: owner and opening balance.
41+
- Output: account summary or an error.
42+
- Edge cases: empty owner; negative opening balance.
43+
44+
2. exercises/02.ts
45+
- Input: minimum and maximum temperatures.
46+
- Output: validated range or an error.
47+
- Edge cases: minimum above maximum; invalid numbers.
48+
49+
## Checkpoint
50+
51+
- [ ] I can encode business rules directly in the constructor.
52+
- [ ] I can keep object state valid after later method calls.
53+
- [ ] I can explain why rejecting invalid construction simplifies later code.
54+
- [ ] I completed exercises/01.ts.
55+
- [ ] I completed exercises/02.ts.
Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
class BankAccount {
2+
#balance: number;
3+
4+
constructor(
5+
readonly owner: string,
6+
openingBalance: number,
7+
) {
8+
if (!owner.trim()) {
9+
throw new Error("Owner name is required.");
10+
}
11+
if (openingBalance < 0) {
12+
throw new Error("Opening balance must be non-negative.");
13+
}
14+
this.#balance = openingBalance;
15+
}
16+
17+
deposit(amount: number): void {
18+
if (amount <= 0) {
19+
throw new Error("Deposit amount must be positive.");
20+
}
21+
this.#balance += amount;
22+
}
23+
24+
withdraw(amount: number): boolean {
25+
if (amount <= 0 || amount > this.#balance) {
26+
return false;
27+
}
28+
this.#balance -= amount;
29+
return true;
30+
}
31+
32+
printStatus(): void {
33+
console.log(`${this.owner}: ${this.#balance.toFixed(2)}`);
34+
}
35+
}
36+
37+
const account = new BankAccount("Ana", 120);
38+
account.deposit(30);
39+
account.withdraw(50);
40+
account.printStatus();
41+
42+
export {};
Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
import * as fs from "node:fs";
2+
3+
class BankAccount {
4+
constructor(
5+
readonly owner: string,
6+
readonly balance: number,
7+
) {
8+
if (!owner.trim()) {
9+
throw new Error("Owner name is required.");
10+
}
11+
if (balance < 0) {
12+
throw new Error("Balance must be non-negative.");
13+
}
14+
}
15+
}
16+
17+
function main(): void {
18+
const tokens = fs
19+
.readFileSync(0, "utf8")
20+
.trim()
21+
.split(/\s+/)
22+
.filter((token) => token.length > 0);
23+
24+
const owner = tokens[0] ?? "";
25+
const openingBalance = Number.parseFloat(tokens[1] ?? "");
26+
27+
try {
28+
const account = new BankAccount(owner, openingBalance);
29+
console.log(`${account.owner}: ${account.balance.toFixed(2)}`);
30+
} catch (error) {
31+
console.log((error as Error).message);
32+
}
33+
}
34+
35+
main();
Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
import * as fs from "node:fs";
2+
3+
class TemperatureRange {
4+
constructor(
5+
readonly minimum: number,
6+
readonly maximum: number,
7+
) {
8+
if (Number.isNaN(minimum) || Number.isNaN(maximum)) {
9+
throw new Error("Expected numeric temperatures.");
10+
}
11+
if (minimum > maximum) {
12+
throw new Error("Minimum cannot exceed maximum.");
13+
}
14+
}
15+
}
16+
17+
function main(): void {
18+
const tokens = fs
19+
.readFileSync(0, "utf8")
20+
.trim()
21+
.split(/\s+/)
22+
.filter((token) => token.length > 0);
23+
24+
const minimum = Number.parseFloat(tokens[0] ?? "");
25+
const maximum = Number.parseFloat(tokens[1] ?? "");
26+
27+
try {
28+
const range = new TemperatureRange(minimum, maximum);
29+
console.log(`Range: ${range.minimum}..${range.maximum}`);
30+
} catch (error) {
31+
console.log((error as Error).message);
32+
}
33+
}
34+
35+
main();
Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,55 @@
1+
# Copy and Move Semantics
2+
3+
This module adapts the C++ ownership topic to TypeScript by focusing on reference behavior, shallow copies, and explicit cloning.
4+
5+
## Learning Metadata
6+
7+
- Difficulty: Advanced.
8+
- Estimated Time: 30-45 minutes.
9+
- Prerequisites: `03-advanced/structs-and-classes` and `02-core/algorithms-basics`.
10+
- Cross-Language Lens: Compare C++ copy and move rules with TypeScript reference sharing, spread copies, and deep-clone helpers.
11+
12+
## Quick Run
13+
14+
~~~bash
15+
npm run build:typescript
16+
node build/typescript/03-advanced/copy-and-move-semantics/example/main.js
17+
~~~
18+
19+
## Topics Covered
20+
21+
- Reference aliasing.
22+
- Shallow copy with spread syntax.
23+
- Deep copy with explicit helper functions.
24+
- Deciding when shared state is safe.
25+
26+
## Common Pitfalls
27+
28+
- Assuming spread syntax deep-copies nested objects.
29+
- Mutating an alias and expecting the original to stay unchanged.
30+
- Cloning blindly without understanding the data shape.
31+
32+
## Exercise Focus
33+
34+
- exercises/01.ts: show shallow copy behavior on a nested object.
35+
- exercises/02.ts: write a deep-clone helper for a small inventory model.
36+
37+
### Exercise Specs
38+
39+
1. exercises/01.ts
40+
- Input: one product name and one quantity.
41+
- Output: original and copied records after a nested update.
42+
- Edge cases: negative quantity should print an error.
43+
44+
2. exercises/02.ts
45+
- Input: item name, quantity, and reserved quantity.
46+
- Output: original and cloned inventory states after a clone-only update.
47+
- Edge cases: invalid counts; negative values.
48+
49+
## Checkpoint
50+
51+
- [ ] I can explain why object variables share references by default.
52+
- [ ] I can tell the difference between shallow and deep copies.
53+
- [ ] I can write an explicit clone helper when nested data requires it.
54+
- [ ] I completed exercises/01.ts.
55+
- [ ] I completed exercises/02.ts.
Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
type InventoryRecord = {
2+
name: string;
3+
counts: {
4+
inStock: number;
5+
reserved: number;
6+
};
7+
};
8+
9+
function cloneInventory(record: InventoryRecord): InventoryRecord {
10+
return {
11+
name: record.name,
12+
counts: {
13+
inStock: record.counts.inStock,
14+
reserved: record.counts.reserved,
15+
},
16+
};
17+
}
18+
19+
const original: InventoryRecord = {
20+
name: "Keyboard",
21+
counts: { inStock: 10, reserved: 2 },
22+
};
23+
24+
const alias = original;
25+
const shallowCopy = { ...original };
26+
const deepCopy = cloneInventory(original);
27+
28+
alias.counts.inStock -= 1;
29+
shallowCopy.counts.reserved += 3;
30+
deepCopy.counts.inStock += 5;
31+
32+
console.log(`Original: ${original.counts.inStock}/${original.counts.reserved}`);
33+
console.log(
34+
`Shallow copy: ${shallowCopy.counts.inStock}/${shallowCopy.counts.reserved}`,
35+
);
36+
console.log(
37+
`Deep copy: ${deepCopy.counts.inStock}/${deepCopy.counts.reserved}`,
38+
);
39+
40+
export {};
Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
import * as fs from "node:fs";
2+
3+
type ProductRecord = {
4+
name: string;
5+
details: {
6+
quantity: number;
7+
};
8+
};
9+
10+
function main(): void {
11+
const tokens = fs
12+
.readFileSync(0, "utf8")
13+
.trim()
14+
.split(/\s+/)
15+
.filter((token) => token.length > 0);
16+
17+
const name = tokens[0] ?? "";
18+
const quantity = Number.parseInt(tokens[1] ?? "", 10);
19+
if (!name || !Number.isInteger(quantity) || quantity < 0) {
20+
console.log("Expected: name nonNegativeQuantity");
21+
return;
22+
}
23+
24+
const original: ProductRecord = { name, details: { quantity } };
25+
const copied = { ...original };
26+
copied.details.quantity += 1;
27+
28+
console.log(`Original quantity: ${original.details.quantity}`);
29+
console.log(`Copied quantity: ${copied.details.quantity}`);
30+
}
31+
32+
main();

0 commit comments

Comments
 (0)