Skip to content

Commit 4ec217f

Browse files
committed
Add TypeScript core track
1 parent fc040fb commit 4ec217f

36 files changed

Lines changed: 1340 additions & 28 deletions

File tree

LANGUAGE_PARITY_MATRIX.md

Lines changed: 10 additions & 10 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` implemented first.
7+
- TypeScript is the newest incremental track with `01-foundations` and `02-core` implemented first.
88
- Status labels:
99
- `Done`: module implemented with example, exercises, and README.
1010
- `Planned`: module not implemented yet, already queued in order.
@@ -29,16 +29,16 @@ Current parity progress in non-C++ tracks:
2929
- C#: `6/6` modules complete
3030
- Go: `6/6` modules complete
3131
- Python: `6/6` modules complete
32-
- TypeScript: `0/6` modules complete, foundations delivered first
32+
- TypeScript: `6/6` modules complete
3333

3434
| Order | Module | C++ | C# | Go | Python | TypeScript |
3535
| --- | --- | --- | --- | --- | --- | --- |
36-
| 1 | input-validation | Done | Done | Done | Done | Planned |
37-
| 2 | algorithms-basics | Done | Done | Done | Done | Planned |
38-
| 3 | file-io-basics | Done | Done | Done | Done | Planned |
39-
| 4 | sorting-and-searching | Done | Done | Done | Done | Planned |
40-
| 5 | maps-and-frequency-counting | Done | Done | Done | Done | Planned |
41-
| 6 | error-handling-and-defensive-programming | Done | Done | Done | Done | Planned |
36+
| 1 | input-validation | Done | Done | Done | Done | Done |
37+
| 2 | algorithms-basics | Done | Done | Done | Done | Done |
38+
| 3 | file-io-basics | Done | Done | Done | Done | Done |
39+
| 4 | sorting-and-searching | Done | Done | Done | Done | Done |
40+
| 5 | maps-and-frequency-counting | Done | Done | Done | Done | Done |
41+
| 6 | error-handling-and-defensive-programming | Done | Done | Done | Done | Done |
4242

4343
## Advanced and Expert
4444

@@ -83,7 +83,7 @@ Status labels:
8383
| Checkpoint | C++ | C# | Go | Python | TypeScript |
8484
| --- | --- | --- | --- | --- | --- |
8585
| 01-foundations | Done | Done | Done | Done | Done |
86-
| 02-core | Done | Done | Done | Done | Planned |
86+
| 02-core | Done | Done | Done | Done | Done |
8787
| 03-advanced | Done | Done | Done | Done | Planned |
8888
| 04-expert | Done | Done | Done | Done | Planned |
8989

@@ -92,6 +92,6 @@ Status labels:
9292
| Checkpoint | C++ | C# | Go | Python | TypeScript |
9393
| --- | --- | --- | --- | --- | --- |
9494
| 01-foundations | Done | Done | Done | Done | Done |
95-
| 02-core | Done | Done | Done | Done | Planned |
95+
| 02-core | Done | Done | Done | Done | Done |
9696
| 03-advanced | Done | Done | Done | Done | Planned |
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 | 8/8 foundations modules, 1/4 projects, 1/4 assessments | Foundations launched; core and above planned |
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 |
5858

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

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
# TypeScript 02 Core
2+
3+
This level extends the TypeScript track with validation, file handling, aggregation, and defensive workflows.
4+
5+
## Learning Metadata
6+
7+
- Difficulty: Intermediate.
8+
- Estimated Time: 4-6 hours across modules, project, and assessment.
9+
- Prerequisites: Completion of `01-foundations` plus either `projects/01-foundations` or `assessments/01-foundations`.
10+
- Study Strategy: Work in order, keep validation helpers small, and revisit parsing decisions before moving into the project and assessment.
11+
12+
## Module Order
13+
14+
1. [input-validation](./input-validation/README.md)
15+
2. [algorithms-basics](./algorithms-basics/README.md)
16+
3. [file-io-basics](./file-io-basics/README.md)
17+
4. [sorting-and-searching](./sorting-and-searching/README.md)
18+
5. [maps-and-frequency-counting](./maps-and-frequency-counting/README.md)
19+
6. [error-handling-and-defensive-programming](./error-handling-and-defensive-programming/README.md)
20+
21+
Track progress in [../CHECKLIST.md](../CHECKLIST.md).
22+
23+
## Study Tip
24+
25+
Use `file-io-basics` and `error-handling-and-defensive-programming` together when you want to understand how real console tools keep working even when input gets messy.
Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,55 @@
1+
# Algorithms Basics
2+
3+
This module introduces core algorithm patterns over arrays in plain TypeScript.
4+
5+
## Learning Metadata
6+
7+
- Difficulty: Intermediate.
8+
- Estimated Time: 30-45 minutes.
9+
- Prerequisites: `01-foundations/arrays-and-vectors` and `01-foundations/functions`.
10+
- Cross-Language Lens: Compare explicit loops with library helpers and notice when beginner-friendly code is clearer than a dense chain of methods.
11+
12+
## Quick Run
13+
14+
~~~bash
15+
npm run build:typescript
16+
node build/typescript/02-core/algorithms-basics/example/main.js
17+
~~~
18+
19+
## Topics Covered
20+
21+
- Linear search for the first matching item.
22+
- One-pass minimum and maximum updates.
23+
- Counting matches while scanning once.
24+
- Combining several statistics in a single traversal.
25+
26+
## Common Pitfalls
27+
28+
- Assuming arrays are non-empty before min/max logic.
29+
- Using multiple passes when one traversal is enough.
30+
- Hiding simple logic inside clever but unreadable helpers.
31+
32+
## Exercise Focus
33+
34+
- exercises/01.ts: find the first index of a target value.
35+
- exercises/02.ts: compute minimum, maximum, and even count in one pass.
36+
37+
### Exercise Specs
38+
39+
1. exercises/01.ts
40+
- Input: integer `n`, then `n` values, then a target.
41+
- Output: first index of target or `-1`.
42+
- Edge cases: `n <= 0`; target not present.
43+
44+
2. exercises/02.ts
45+
- Input: integer `n`, then `n` values.
46+
- Output: minimum, maximum, and even count.
47+
- Edge cases: all odd numbers; all equal values; `n <= 0`.
48+
49+
## Checkpoint
50+
51+
- [ ] I can implement linear search without relying on hidden helpers.
52+
- [ ] I can compute multiple statistics in a single traversal.
53+
- [ ] I can protect min/max logic against empty input.
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+
function firstIndexOf(values: number[], target: number): number {
2+
for (let index = 0; index < values.length; index += 1) {
3+
if (values[index] === target) {
4+
return index;
5+
}
6+
}
7+
return -1;
8+
}
9+
10+
function summarize(values: number[]): {
11+
minimum: number;
12+
maximum: number;
13+
evenCount: number;
14+
} {
15+
let minimum = values[0]!;
16+
let maximum = values[0]!;
17+
let evenCount = 0;
18+
19+
for (const value of values) {
20+
if (value < minimum) {
21+
minimum = value;
22+
}
23+
if (value > maximum) {
24+
maximum = value;
25+
}
26+
if (value % 2 === 0) {
27+
evenCount += 1;
28+
}
29+
}
30+
31+
return { minimum, maximum, evenCount };
32+
}
33+
34+
const values = [14, 7, 22, 14, 9, 18];
35+
const summary = summarize(values);
36+
console.log(`Values: ${values.join(", ")}`);
37+
console.log(`First index of 14: ${firstIndexOf(values, 14)}`);
38+
console.log(`Minimum: ${summary.minimum}`);
39+
console.log(`Maximum: ${summary.maximum}`);
40+
console.log(`Even count: ${summary.evenCount}`);
41+
42+
export {};
Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
import * as fs from "node:fs";
2+
3+
function main(): void {
4+
const tokens = fs
5+
.readFileSync(0, "utf8")
6+
.trim()
7+
.split(/\s+/)
8+
.filter((token) => token.length > 0);
9+
10+
const count = Number.parseInt(tokens[0] ?? "", 10);
11+
if (!Number.isInteger(count) || count <= 0) {
12+
console.log("Count must be a positive integer.");
13+
return;
14+
}
15+
16+
const values = tokens
17+
.slice(1, count + 1)
18+
.map((token) => Number.parseInt(token, 10));
19+
const target = Number.parseInt(tokens[count + 1] ?? "", 10);
20+
21+
if (
22+
values.length !== count ||
23+
values.some((value) => !Number.isInteger(value)) ||
24+
!Number.isInteger(target)
25+
) {
26+
console.log("Invalid algorithm input.");
27+
return;
28+
}
29+
30+
let foundIndex = -1;
31+
for (let index = 0; index < values.length; index += 1) {
32+
if (values[index] === target) {
33+
foundIndex = index;
34+
break;
35+
}
36+
}
37+
38+
console.log(`First index: ${foundIndex}`);
39+
}
40+
41+
main();
Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
import * as fs from "node:fs";
2+
3+
function main(): void {
4+
const tokens = fs
5+
.readFileSync(0, "utf8")
6+
.trim()
7+
.split(/\s+/)
8+
.filter((token) => token.length > 0);
9+
10+
const count = Number.parseInt(tokens[0] ?? "", 10);
11+
if (!Number.isInteger(count) || count <= 0) {
12+
console.log("Count must be a positive integer.");
13+
return;
14+
}
15+
16+
const values = tokens
17+
.slice(1, count + 1)
18+
.map((token) => Number.parseInt(token, 10));
19+
if (
20+
values.length !== count ||
21+
values.some((value) => !Number.isInteger(value))
22+
) {
23+
console.log("Invalid value list.");
24+
return;
25+
}
26+
27+
let minimum = values[0]!;
28+
let maximum = values[0]!;
29+
let evenCount = 0;
30+
31+
for (const value of values) {
32+
if (value < minimum) {
33+
minimum = value;
34+
}
35+
if (value > maximum) {
36+
maximum = value;
37+
}
38+
if (value % 2 === 0) {
39+
evenCount += 1;
40+
}
41+
}
42+
43+
console.log(`Minimum: ${minimum}`);
44+
console.log(`Maximum: ${maximum}`);
45+
console.log(`Even count: ${evenCount}`);
46+
}
47+
48+
main();
Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,55 @@
1+
# Error Handling and Defensive Programming
2+
3+
This module teaches TypeScript programs to reject bad data cleanly without losing control flow.
4+
5+
## Learning Metadata
6+
7+
- Difficulty: Intermediate.
8+
- Estimated Time: 30-45 minutes.
9+
- Prerequisites: `02-core/input-validation` and `02-core/file-io-basics`.
10+
- Cross-Language Lens: Compare guard clauses, nullable parse results, and selective `try/catch` with the defensive styles used in the other tracks.
11+
12+
## Quick Run
13+
14+
~~~bash
15+
npm run build:typescript
16+
node build/typescript/02-core/error-handling-and-defensive-programming/example/main.js
17+
~~~
18+
19+
## Topics Covered
20+
21+
- Guard clauses that stop unsafe work early.
22+
- Parse helpers that return `null` instead of half-valid data.
23+
- Catching only the errors you actually expect.
24+
- Preserving valid work even when part of the input is malformed.
25+
26+
## Common Pitfalls
27+
28+
- Catching every error without improving the message.
29+
- Pushing invalid data forward just to avoid returning early.
30+
- Mixing user-facing errors with internal debugging details.
31+
32+
## Exercise Focus
33+
34+
- exercises/01.ts: safely parse one `name score` record.
35+
- exercises/02.ts: ignore invalid numeric tokens and summarize the valid ones.
36+
37+
### Exercise Specs
38+
39+
1. exercises/01.ts
40+
- Input: one line shaped like `Full Name Score`.
41+
- Output: accepted record or an error message.
42+
- Edge cases: missing name; non-integer score; score outside `0..100`.
43+
44+
2. exercises/02.ts
45+
- Input: one line of mixed tokens.
46+
- Output: valid count and average of accepted integers.
47+
- Edge cases: no valid integers; malformed tokens between valid numbers.
48+
49+
## Checkpoint
50+
51+
- [ ] I can stop bad input before it pollutes later steps.
52+
- [ ] I can return safe parse results instead of throwing for routine validation.
53+
- [ ] I can keep useful work even when some rows fail.
54+
- [ ] I completed exercises/01.ts.
55+
- [ ] I completed exercises/02.ts.
Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
type ScoreRecord = {
2+
name: string;
3+
score: number;
4+
};
5+
6+
function parseRecord(line: string): ScoreRecord | null {
7+
const parts = line.trim().split(/\s+/);
8+
if (parts.length < 2) {
9+
return null;
10+
}
11+
12+
const score = Number.parseInt(parts.at(-1) ?? "", 10);
13+
if (!Number.isInteger(score) || score < 0 || score > 100) {
14+
return null;
15+
}
16+
17+
const name = parts.slice(0, -1).join(" ");
18+
if (!name) {
19+
return null;
20+
}
21+
22+
return { name, score };
23+
}
24+
25+
const rows = [
26+
"Ana Smith 91",
27+
"InvalidRow",
28+
"Bob Lee ninety",
29+
"Carla Mendez 88",
30+
];
31+
const accepted: ScoreRecord[] = [];
32+
33+
for (const row of rows) {
34+
const record = parseRecord(row);
35+
if (record === null) {
36+
console.log(`Skipped invalid row: ${row}`);
37+
continue;
38+
}
39+
accepted.push(record);
40+
}
41+
42+
console.log(`Accepted rows: ${accepted.length}`);
43+
for (const record of accepted) {
44+
console.log(`- ${record.name}: ${record.score}`);
45+
}
46+
47+
export {};

0 commit comments

Comments
 (0)