@shaykec/bridge 0.4.25 → 0.4.26
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/journeys/ai-engineer.yaml +34 -0
- package/journeys/backend-developer.yaml +36 -0
- package/journeys/business-analyst.yaml +37 -0
- package/journeys/devops-engineer.yaml +37 -0
- package/journeys/engineering-manager.yaml +44 -0
- package/journeys/frontend-developer.yaml +41 -0
- package/journeys/fullstack-developer.yaml +49 -0
- package/journeys/mobile-developer.yaml +42 -0
- package/journeys/product-manager.yaml +35 -0
- package/journeys/qa-engineer.yaml +37 -0
- package/journeys/ux-designer.yaml +43 -0
- package/modules/README.md +52 -0
- package/modules/accessibility-fundamentals/content.md +126 -0
- package/modules/accessibility-fundamentals/exercises.md +88 -0
- package/modules/accessibility-fundamentals/module.yaml +43 -0
- package/modules/accessibility-fundamentals/quick-ref.md +71 -0
- package/modules/accessibility-fundamentals/quiz.md +100 -0
- package/modules/accessibility-fundamentals/resources.md +29 -0
- package/modules/accessibility-fundamentals/walkthrough.md +80 -0
- package/modules/adr-writing/content.md +121 -0
- package/modules/adr-writing/exercises.md +81 -0
- package/modules/adr-writing/module.yaml +41 -0
- package/modules/adr-writing/quick-ref.md +57 -0
- package/modules/adr-writing/quiz.md +73 -0
- package/modules/adr-writing/resources.md +29 -0
- package/modules/adr-writing/walkthrough.md +64 -0
- package/modules/ai-agents/content.md +120 -0
- package/modules/ai-agents/exercises.md +82 -0
- package/modules/ai-agents/module.yaml +42 -0
- package/modules/ai-agents/quick-ref.md +60 -0
- package/modules/ai-agents/quiz.md +103 -0
- package/modules/ai-agents/resources.md +30 -0
- package/modules/ai-agents/walkthrough.md +85 -0
- package/modules/ai-assisted-research/content.md +136 -0
- package/modules/ai-assisted-research/exercises.md +80 -0
- package/modules/ai-assisted-research/module.yaml +42 -0
- package/modules/ai-assisted-research/quick-ref.md +67 -0
- package/modules/ai-assisted-research/quiz.md +73 -0
- package/modules/ai-assisted-research/resources.md +33 -0
- package/modules/ai-assisted-research/walkthrough.md +85 -0
- package/modules/ai-pair-programming/content.md +105 -0
- package/modules/ai-pair-programming/exercises.md +98 -0
- package/modules/ai-pair-programming/module.yaml +39 -0
- package/modules/ai-pair-programming/quick-ref.md +58 -0
- package/modules/ai-pair-programming/quiz.md +73 -0
- package/modules/ai-pair-programming/resources.md +34 -0
- package/modules/ai-pair-programming/walkthrough.md +117 -0
- package/modules/ai-test-generation/content.md +125 -0
- package/modules/ai-test-generation/exercises.md +98 -0
- package/modules/ai-test-generation/module.yaml +39 -0
- package/modules/ai-test-generation/quick-ref.md +65 -0
- package/modules/ai-test-generation/quiz.md +74 -0
- package/modules/ai-test-generation/resources.md +41 -0
- package/modules/ai-test-generation/walkthrough.md +100 -0
- package/modules/api-design/content.md +189 -0
- package/modules/api-design/exercises.md +84 -0
- package/modules/api-design/game.yaml +113 -0
- package/modules/api-design/module.yaml +45 -0
- package/modules/api-design/quick-ref.md +73 -0
- package/modules/api-design/quiz.md +100 -0
- package/modules/api-design/resources.md +55 -0
- package/modules/api-design/walkthrough.md +88 -0
- package/modules/clean-code/content.md +136 -0
- package/modules/clean-code/exercises.md +137 -0
- package/modules/clean-code/game.yaml +172 -0
- package/modules/clean-code/module.yaml +44 -0
- package/modules/clean-code/quick-ref.md +44 -0
- package/modules/clean-code/quiz.md +105 -0
- package/modules/clean-code/resources.md +40 -0
- package/modules/clean-code/walkthrough.md +78 -0
- package/modules/clean-code/workshop.yaml +149 -0
- package/modules/code-review/content.md +130 -0
- package/modules/code-review/exercises.md +95 -0
- package/modules/code-review/game.yaml +83 -0
- package/modules/code-review/module.yaml +42 -0
- package/modules/code-review/quick-ref.md +77 -0
- package/modules/code-review/quiz.md +105 -0
- package/modules/code-review/resources.md +40 -0
- package/modules/code-review/walkthrough.md +106 -0
- package/modules/daily-workflow/content.md +81 -0
- package/modules/daily-workflow/exercises.md +50 -0
- package/modules/daily-workflow/module.yaml +33 -0
- package/modules/daily-workflow/quick-ref.md +37 -0
- package/modules/daily-workflow/quiz.md +65 -0
- package/modules/daily-workflow/resources.md +38 -0
- package/modules/daily-workflow/walkthrough.md +83 -0
- package/modules/debugging-systematically/content.md +139 -0
- package/modules/debugging-systematically/exercises.md +91 -0
- package/modules/debugging-systematically/module.yaml +46 -0
- package/modules/debugging-systematically/quick-ref.md +59 -0
- package/modules/debugging-systematically/quiz.md +105 -0
- package/modules/debugging-systematically/resources.md +42 -0
- package/modules/debugging-systematically/walkthrough.md +84 -0
- package/modules/debugging-systematically/workshop.yaml +127 -0
- package/modules/demo-test/content.md +68 -0
- package/modules/demo-test/exercises.md +28 -0
- package/modules/demo-test/game.yaml +171 -0
- package/modules/demo-test/module.yaml +41 -0
- package/modules/demo-test/quick-ref.md +54 -0
- package/modules/demo-test/quiz.md +74 -0
- package/modules/demo-test/resources.md +21 -0
- package/modules/demo-test/walkthrough.md +122 -0
- package/modules/demo-test/workshop.yaml +31 -0
- package/modules/design-critique/content.md +93 -0
- package/modules/design-critique/exercises.md +71 -0
- package/modules/design-critique/module.yaml +41 -0
- package/modules/design-critique/quick-ref.md +63 -0
- package/modules/design-critique/quiz.md +73 -0
- package/modules/design-critique/resources.md +27 -0
- package/modules/design-critique/walkthrough.md +68 -0
- package/modules/design-patterns/content.md +335 -0
- package/modules/design-patterns/exercises.md +82 -0
- package/modules/design-patterns/game.yaml +55 -0
- package/modules/design-patterns/module.yaml +45 -0
- package/modules/design-patterns/quick-ref.md +44 -0
- package/modules/design-patterns/quiz.md +101 -0
- package/modules/design-patterns/resources.md +40 -0
- package/modules/design-patterns/walkthrough.md +64 -0
- package/modules/exploratory-testing/content.md +133 -0
- package/modules/exploratory-testing/exercises.md +88 -0
- package/modules/exploratory-testing/module.yaml +41 -0
- package/modules/exploratory-testing/quick-ref.md +68 -0
- package/modules/exploratory-testing/quiz.md +75 -0
- package/modules/exploratory-testing/resources.md +39 -0
- package/modules/exploratory-testing/walkthrough.md +87 -0
- package/modules/git/content.md +128 -0
- package/modules/git/exercises.md +53 -0
- package/modules/git/game.yaml +190 -0
- package/modules/git/module.yaml +44 -0
- package/modules/git/quick-ref.md +67 -0
- package/modules/git/quiz.md +89 -0
- package/modules/git/resources.md +49 -0
- package/modules/git/walkthrough.md +92 -0
- package/modules/git/workshop.yaml +145 -0
- package/modules/hiring-interviews/content.md +130 -0
- package/modules/hiring-interviews/exercises.md +88 -0
- package/modules/hiring-interviews/module.yaml +41 -0
- package/modules/hiring-interviews/quick-ref.md +68 -0
- package/modules/hiring-interviews/quiz.md +73 -0
- package/modules/hiring-interviews/resources.md +36 -0
- package/modules/hiring-interviews/walkthrough.md +75 -0
- package/modules/hooks/content.md +97 -0
- package/modules/hooks/exercises.md +69 -0
- package/modules/hooks/module.yaml +39 -0
- package/modules/hooks/quick-ref.md +93 -0
- package/modules/hooks/quiz.md +81 -0
- package/modules/hooks/resources.md +34 -0
- package/modules/hooks/walkthrough.md +105 -0
- package/modules/hooks/workshop.yaml +64 -0
- package/modules/incident-response/content.md +124 -0
- package/modules/incident-response/exercises.md +82 -0
- package/modules/incident-response/game.yaml +132 -0
- package/modules/incident-response/module.yaml +45 -0
- package/modules/incident-response/quick-ref.md +53 -0
- package/modules/incident-response/quiz.md +103 -0
- package/modules/incident-response/resources.md +40 -0
- package/modules/incident-response/walkthrough.md +82 -0
- package/modules/llm-fundamentals/content.md +114 -0
- package/modules/llm-fundamentals/exercises.md +83 -0
- package/modules/llm-fundamentals/module.yaml +42 -0
- package/modules/llm-fundamentals/quick-ref.md +64 -0
- package/modules/llm-fundamentals/quiz.md +103 -0
- package/modules/llm-fundamentals/resources.md +30 -0
- package/modules/llm-fundamentals/walkthrough.md +91 -0
- package/modules/one-on-ones/content.md +133 -0
- package/modules/one-on-ones/exercises.md +81 -0
- package/modules/one-on-ones/module.yaml +44 -0
- package/modules/one-on-ones/quick-ref.md +67 -0
- package/modules/one-on-ones/quiz.md +73 -0
- package/modules/one-on-ones/resources.md +37 -0
- package/modules/one-on-ones/walkthrough.md +69 -0
- package/modules/package.json +9 -0
- package/modules/prioritization-frameworks/content.md +130 -0
- package/modules/prioritization-frameworks/exercises.md +93 -0
- package/modules/prioritization-frameworks/module.yaml +41 -0
- package/modules/prioritization-frameworks/quick-ref.md +77 -0
- package/modules/prioritization-frameworks/quiz.md +73 -0
- package/modules/prioritization-frameworks/resources.md +32 -0
- package/modules/prioritization-frameworks/walkthrough.md +69 -0
- package/modules/prompt-engineering/content.md +123 -0
- package/modules/prompt-engineering/exercises.md +82 -0
- package/modules/prompt-engineering/game.yaml +101 -0
- package/modules/prompt-engineering/module.yaml +45 -0
- package/modules/prompt-engineering/quick-ref.md +65 -0
- package/modules/prompt-engineering/quiz.md +105 -0
- package/modules/prompt-engineering/resources.md +36 -0
- package/modules/prompt-engineering/walkthrough.md +81 -0
- package/modules/rag-fundamentals/content.md +111 -0
- package/modules/rag-fundamentals/exercises.md +80 -0
- package/modules/rag-fundamentals/module.yaml +45 -0
- package/modules/rag-fundamentals/quick-ref.md +58 -0
- package/modules/rag-fundamentals/quiz.md +75 -0
- package/modules/rag-fundamentals/resources.md +34 -0
- package/modules/rag-fundamentals/walkthrough.md +75 -0
- package/modules/react-fundamentals/content.md +140 -0
- package/modules/react-fundamentals/exercises.md +81 -0
- package/modules/react-fundamentals/game.yaml +145 -0
- package/modules/react-fundamentals/module.yaml +45 -0
- package/modules/react-fundamentals/quick-ref.md +62 -0
- package/modules/react-fundamentals/quiz.md +106 -0
- package/modules/react-fundamentals/resources.md +42 -0
- package/modules/react-fundamentals/walkthrough.md +89 -0
- package/modules/react-fundamentals/workshop.yaml +112 -0
- package/modules/react-native-fundamentals/content.md +141 -0
- package/modules/react-native-fundamentals/exercises.md +79 -0
- package/modules/react-native-fundamentals/module.yaml +42 -0
- package/modules/react-native-fundamentals/quick-ref.md +60 -0
- package/modules/react-native-fundamentals/quiz.md +61 -0
- package/modules/react-native-fundamentals/resources.md +24 -0
- package/modules/react-native-fundamentals/walkthrough.md +84 -0
- package/modules/registry.yaml +1650 -0
- package/modules/risk-management/content.md +162 -0
- package/modules/risk-management/exercises.md +86 -0
- package/modules/risk-management/module.yaml +41 -0
- package/modules/risk-management/quick-ref.md +82 -0
- package/modules/risk-management/quiz.md +73 -0
- package/modules/risk-management/resources.md +40 -0
- package/modules/risk-management/walkthrough.md +67 -0
- package/modules/running-effective-standups/content.md +119 -0
- package/modules/running-effective-standups/exercises.md +79 -0
- package/modules/running-effective-standups/module.yaml +40 -0
- package/modules/running-effective-standups/quick-ref.md +61 -0
- package/modules/running-effective-standups/quiz.md +73 -0
- package/modules/running-effective-standups/resources.md +36 -0
- package/modules/running-effective-standups/walkthrough.md +76 -0
- package/modules/solid-principles/content.md +154 -0
- package/modules/solid-principles/exercises.md +107 -0
- package/modules/solid-principles/module.yaml +42 -0
- package/modules/solid-principles/quick-ref.md +50 -0
- package/modules/solid-principles/quiz.md +102 -0
- package/modules/solid-principles/resources.md +39 -0
- package/modules/solid-principles/walkthrough.md +84 -0
- package/modules/sprint-planning/content.md +142 -0
- package/modules/sprint-planning/exercises.md +79 -0
- package/modules/sprint-planning/game.yaml +84 -0
- package/modules/sprint-planning/module.yaml +44 -0
- package/modules/sprint-planning/quick-ref.md +76 -0
- package/modules/sprint-planning/quiz.md +102 -0
- package/modules/sprint-planning/resources.md +39 -0
- package/modules/sprint-planning/walkthrough.md +75 -0
- package/modules/sql-fundamentals/content.md +160 -0
- package/modules/sql-fundamentals/exercises.md +87 -0
- package/modules/sql-fundamentals/game.yaml +105 -0
- package/modules/sql-fundamentals/module.yaml +45 -0
- package/modules/sql-fundamentals/quick-ref.md +53 -0
- package/modules/sql-fundamentals/quiz.md +103 -0
- package/modules/sql-fundamentals/resources.md +42 -0
- package/modules/sql-fundamentals/walkthrough.md +92 -0
- package/modules/sql-fundamentals/workshop.yaml +109 -0
- package/modules/stakeholder-communication/content.md +186 -0
- package/modules/stakeholder-communication/exercises.md +87 -0
- package/modules/stakeholder-communication/module.yaml +38 -0
- package/modules/stakeholder-communication/quick-ref.md +89 -0
- package/modules/stakeholder-communication/quiz.md +73 -0
- package/modules/stakeholder-communication/resources.md +41 -0
- package/modules/stakeholder-communication/walkthrough.md +74 -0
- package/modules/system-design/content.md +149 -0
- package/modules/system-design/exercises.md +83 -0
- package/modules/system-design/game.yaml +95 -0
- package/modules/system-design/module.yaml +46 -0
- package/modules/system-design/quick-ref.md +59 -0
- package/modules/system-design/quiz.md +102 -0
- package/modules/system-design/resources.md +46 -0
- package/modules/system-design/walkthrough.md +90 -0
- package/modules/team-topologies/content.md +166 -0
- package/modules/team-topologies/exercises.md +85 -0
- package/modules/team-topologies/module.yaml +41 -0
- package/modules/team-topologies/quick-ref.md +61 -0
- package/modules/team-topologies/quiz.md +101 -0
- package/modules/team-topologies/resources.md +37 -0
- package/modules/team-topologies/walkthrough.md +76 -0
- package/modules/technical-debt/content.md +111 -0
- package/modules/technical-debt/exercises.md +92 -0
- package/modules/technical-debt/module.yaml +39 -0
- package/modules/technical-debt/quick-ref.md +60 -0
- package/modules/technical-debt/quiz.md +73 -0
- package/modules/technical-debt/resources.md +25 -0
- package/modules/technical-debt/walkthrough.md +94 -0
- package/modules/technical-mentoring/content.md +128 -0
- package/modules/technical-mentoring/exercises.md +84 -0
- package/modules/technical-mentoring/module.yaml +41 -0
- package/modules/technical-mentoring/quick-ref.md +74 -0
- package/modules/technical-mentoring/quiz.md +73 -0
- package/modules/technical-mentoring/resources.md +33 -0
- package/modules/technical-mentoring/walkthrough.md +65 -0
- package/modules/test-strategy/content.md +136 -0
- package/modules/test-strategy/exercises.md +84 -0
- package/modules/test-strategy/game.yaml +99 -0
- package/modules/test-strategy/module.yaml +45 -0
- package/modules/test-strategy/quick-ref.md +66 -0
- package/modules/test-strategy/quiz.md +99 -0
- package/modules/test-strategy/resources.md +60 -0
- package/modules/test-strategy/walkthrough.md +97 -0
- package/modules/test-strategy/workshop.yaml +96 -0
- package/modules/typescript-fundamentals/content.md +127 -0
- package/modules/typescript-fundamentals/exercises.md +79 -0
- package/modules/typescript-fundamentals/game.yaml +111 -0
- package/modules/typescript-fundamentals/module.yaml +45 -0
- package/modules/typescript-fundamentals/quick-ref.md +55 -0
- package/modules/typescript-fundamentals/quiz.md +104 -0
- package/modules/typescript-fundamentals/resources.md +42 -0
- package/modules/typescript-fundamentals/walkthrough.md +71 -0
- package/modules/typescript-fundamentals/workshop.yaml +146 -0
- package/modules/user-story-mapping/content.md +123 -0
- package/modules/user-story-mapping/exercises.md +87 -0
- package/modules/user-story-mapping/module.yaml +41 -0
- package/modules/user-story-mapping/quick-ref.md +64 -0
- package/modules/user-story-mapping/quiz.md +73 -0
- package/modules/user-story-mapping/resources.md +29 -0
- package/modules/user-story-mapping/walkthrough.md +86 -0
- package/modules/writing-prds/content.md +133 -0
- package/modules/writing-prds/exercises.md +93 -0
- package/modules/writing-prds/game.yaml +83 -0
- package/modules/writing-prds/module.yaml +44 -0
- package/modules/writing-prds/quick-ref.md +77 -0
- package/modules/writing-prds/quiz.md +103 -0
- package/modules/writing-prds/resources.md +30 -0
- package/modules/writing-prds/walkthrough.md +87 -0
- package/package.json +1 -1
|
@@ -0,0 +1,97 @@
|
|
|
1
|
+
# Test Strategy Walkthrough — Learn by Doing
|
|
2
|
+
|
|
3
|
+
## Before We Begin
|
|
4
|
+
|
|
5
|
+
**Diagnostic Question:** When you hear "test pyramid," what do you picture? Where would unit tests, integration tests, and e2e tests sit — and why does that shape matter?
|
|
6
|
+
|
|
7
|
+
**Checkpoint:** You have a rough mental model of the test pyramid (many unit tests at the base, fewer e2e at the top). You're ready to think about what belongs at each layer.
|
|
8
|
+
|
|
9
|
+
---
|
|
10
|
+
|
|
11
|
+
## Step 1: Identify Test Layers for a Feature
|
|
12
|
+
|
|
13
|
+
You're building a "Forgot Password" flow: user enters email → receives reset link → clicks link → sets new password → logs in.
|
|
14
|
+
|
|
15
|
+
<!-- hint:diagram mermaid-type="flowchart" topic="test pyramid layers" -->
|
|
16
|
+
<!-- hint:buttons type="single" prompt="Which layer verifies the full user flow?" options="Unit,Integration,E2E" -->
|
|
17
|
+
|
|
18
|
+
**Task:** For this flow, list one example of (a) a unit test, (b) an integration test, and (c) an e2e test. What does each layer verify that the others don't?
|
|
19
|
+
|
|
20
|
+
**Question:** Why would you want more unit tests than e2e tests for this flow? What happens if you only have e2e tests?
|
|
21
|
+
|
|
22
|
+
**Checkpoint:** User identifies: unit = validate email format, hash password logic; integration = email service sends, token storage/retrieval; e2e = full click-through flow. They understand unit = fast feedback, e2e = confidence but slow.
|
|
23
|
+
|
|
24
|
+
---
|
|
25
|
+
|
|
26
|
+
## Step 2: What to Test vs What Not to Test
|
|
27
|
+
|
|
28
|
+
<!-- hint:card type="concept" title="Worth testing" -->
|
|
29
|
+
|
|
30
|
+
Consider this function:
|
|
31
|
+
|
|
32
|
+
```javascript
|
|
33
|
+
function formatCurrency(amount) {
|
|
34
|
+
return new Intl.NumberFormat('en-US', {
|
|
35
|
+
style: 'currency',
|
|
36
|
+
currency: 'USD'
|
|
37
|
+
}).format(amount);
|
|
38
|
+
}
|
|
39
|
+
```
|
|
40
|
+
|
|
41
|
+
**Task:** Should you test this? If yes, what would you assert? If no, why not? Now consider a function that computes tax: `function computeTax(subtotal, state) { ... }`. What would you test there?
|
|
42
|
+
|
|
43
|
+
**Question:** What makes one function "worth testing" and another "skip it"?
|
|
44
|
+
|
|
45
|
+
**Checkpoint:** User sees formatCurrency as thin wrapper over Intl — maybe one smoke test. computeTax has business logic (rates, edge cases) — definitely test. Worth testing = non-trivial logic, business rules, bug-prone code.
|
|
46
|
+
|
|
47
|
+
---
|
|
48
|
+
|
|
49
|
+
## Step 3: Choose a Test Double
|
|
50
|
+
|
|
51
|
+
<!-- hint:list style="cards" -->
|
|
52
|
+
|
|
53
|
+
You're testing a function `processOrder(order)` that calls `paymentGateway.charge()` and `inventoryService.reserve()`. You want to verify the order is saved correctly when payment succeeds.
|
|
54
|
+
|
|
55
|
+
**Task:** Would you use a stub, mock, or spy for `paymentGateway` and `inventoryService`? Why? What would you verify in your test?
|
|
56
|
+
|
|
57
|
+
**Question:** When does "verify it was called" matter vs "just make it return success"?
|
|
58
|
+
|
|
59
|
+
**Checkpoint:** User chooses: stub for both if they only care about order persistence (fake success responses). Mock if they need to verify charge was called with correct amount. Spy if they want to assert call count or args after the fact.
|
|
60
|
+
|
|
61
|
+
---
|
|
62
|
+
|
|
63
|
+
## Step 4: Design a Test Pyramid for Your Project
|
|
64
|
+
|
|
65
|
+
<!-- hint:diagram mermaid-type="flowchart" topic="test pyramid distribution" -->
|
|
66
|
+
|
|
67
|
+
Imagine a small API: user registration, login, and a "list my orders" endpoint. You have limited time.
|
|
68
|
+
|
|
69
|
+
**Task:** Sketch a test pyramid: how many tests at each layer? What would be your first 5 tests to write?
|
|
70
|
+
|
|
71
|
+
**Question:** If you could only have 3 tests total, which would you choose and why?
|
|
72
|
+
|
|
73
|
+
**Checkpoint:** User proposes pyramid: many unit tests for validation, auth logic, order filtering; some integration for DB; 1–2 e2e for critical flows. If only 3: maybe 1 e2e (login + list orders), 1 integration (auth), 1 unit (password validation). Prioritizes highest risk.
|
|
74
|
+
|
|
75
|
+
---
|
|
76
|
+
|
|
77
|
+
## Step 5: Avoid Implementation-Detail Assertions
|
|
78
|
+
|
|
79
|
+
A component fetches users and displays them. Someone writes: "Assert that `useEffect` was called twice."
|
|
80
|
+
|
|
81
|
+
**Task:** Why is that a bad assertion? What would you assert instead?
|
|
82
|
+
|
|
83
|
+
**Question:** How do you know if an assertion is testing implementation vs behavior?
|
|
84
|
+
|
|
85
|
+
**Checkpoint:** User recognizes: useEffect count is implementation; refactoring (e.g., to a different hook) would break the test without changing behavior. Better: assert rendered output (user names on screen) or that the correct API was called. Behavior = what the user sees or what the system does; implementation = how it's built.
|
|
86
|
+
|
|
87
|
+
---
|
|
88
|
+
|
|
89
|
+
## Step 6: Place Tests in CI/CD
|
|
90
|
+
|
|
91
|
+
Your pipeline runs: lint → build → tests → deploy. Tests currently take 12 minutes (unit 2 min, integration 5 min, e2e 5 min).
|
|
92
|
+
|
|
93
|
+
**Task:** How would you structure the pipeline so developers get fast feedback? What would block a merge vs what could run after?
|
|
94
|
+
|
|
95
|
+
**Question:** Why might you run e2e only on main, not on every PR?
|
|
96
|
+
|
|
97
|
+
**Checkpoint:** User suggests: run unit + integration on every PR (7 min); run e2e on merge to main or nightly. Fast feedback for 95% of issues; e2e catches integration bugs less frequently. Cost/speed vs confidence tradeoff.
|
|
@@ -0,0 +1,96 @@
|
|
|
1
|
+
scenarios:
|
|
2
|
+
- id: unit-test-tdd
|
|
3
|
+
title: "Red-Green-Refactor with Unit Tests"
|
|
4
|
+
difficulty: beginner
|
|
5
|
+
xp: 30
|
|
6
|
+
setup:
|
|
7
|
+
- "mkdir -p workshop && cd workshop"
|
|
8
|
+
- "npm init -y"
|
|
9
|
+
- "npm install --save-dev vitest"
|
|
10
|
+
files:
|
|
11
|
+
- name: calculator.js
|
|
12
|
+
content: |
|
|
13
|
+
function add(a, b) { return 0; }
|
|
14
|
+
function subtract(a, b) { return 0; }
|
|
15
|
+
function multiply(a, b) { return 0; }
|
|
16
|
+
function divide(a, b) { return 0; }
|
|
17
|
+
module.exports = { add, subtract, multiply, divide };
|
|
18
|
+
language: javascript
|
|
19
|
+
readonly: false
|
|
20
|
+
- name: calculator.test.js
|
|
21
|
+
content: |
|
|
22
|
+
const { add, subtract, multiply, divide } = require('./calculator');
|
|
23
|
+
const assert = require('assert');
|
|
24
|
+
assert.strictEqual(add(2, 3), 5, 'add(2,3) should be 5');
|
|
25
|
+
assert.strictEqual(subtract(5, 2), 3, 'subtract(5,2) should be 3');
|
|
26
|
+
assert.strictEqual(multiply(4, 3), 12, 'multiply(4,3) should be 12');
|
|
27
|
+
assert.strictEqual(divide(10, 2), 5, 'divide(10,2) should be 5');
|
|
28
|
+
console.log('All tests passed');
|
|
29
|
+
language: javascript
|
|
30
|
+
readonly: true
|
|
31
|
+
task: "Implement add, subtract, multiply, and divide in calculator.js so that node calculator.test.js prints 'All tests passed'. No test runner—the test file runs directly with node."
|
|
32
|
+
validations:
|
|
33
|
+
- label: "All tests passed"
|
|
34
|
+
check: output-contains
|
|
35
|
+
pattern: "All tests passed"
|
|
36
|
+
hints:
|
|
37
|
+
- "The test file requires add, subtract, multiply, divide from calculator.js. Export them."
|
|
38
|
+
- "add(a,b) returns a+b. subtract returns a-b, multiply returns a*b, divide returns a/b."
|
|
39
|
+
- "module.exports = { add, subtract, multiply, divide };"
|
|
40
|
+
agent_prompts:
|
|
41
|
+
on_start: "What do the failing tests tell you about what's missing? How would you add the smallest code to make one test pass?"
|
|
42
|
+
|
|
43
|
+
- id: integration-test
|
|
44
|
+
title: "Write Integration Tests for a Service"
|
|
45
|
+
difficulty: intermediate
|
|
46
|
+
xp: 40
|
|
47
|
+
setup:
|
|
48
|
+
- "mkdir -p workshop && cd workshop"
|
|
49
|
+
- "npm init -y"
|
|
50
|
+
files:
|
|
51
|
+
- name: userService.js
|
|
52
|
+
content: |
|
|
53
|
+
const users = new Map([
|
|
54
|
+
[1, { id: 1, name: 'Alice', email: 'alice@example.com' }],
|
|
55
|
+
[2, { id: 2, name: 'Bob', email: 'bob@example.com' }],
|
|
56
|
+
]);
|
|
57
|
+
function getUserById(id) {
|
|
58
|
+
return users.get(id) ?? null;
|
|
59
|
+
}
|
|
60
|
+
function getAllUsers() {
|
|
61
|
+
return Array.from(users.values());
|
|
62
|
+
}
|
|
63
|
+
function createUser({ name, email }) {
|
|
64
|
+
const id = users.size + 1;
|
|
65
|
+
users.set(id, { id, name, email });
|
|
66
|
+
return users.get(id);
|
|
67
|
+
}
|
|
68
|
+
module.exports = { getUserById, getAllUsers, createUser };
|
|
69
|
+
language: javascript
|
|
70
|
+
readonly: true
|
|
71
|
+
- name: userService.test.js
|
|
72
|
+
content: |
|
|
73
|
+
// TODO: Write tests that verify:
|
|
74
|
+
// 1. getUserById(1) returns a user with name 'Alice'
|
|
75
|
+
// 2. getAllUsers() returns at least 2 users
|
|
76
|
+
// 3. createUser({ name: 'Carol', email: 'carol@example.com' }) adds a new user and returns it
|
|
77
|
+
const { getUserById, getAllUsers, createUser } = require('./userService');
|
|
78
|
+
const assert = require('assert');
|
|
79
|
+
// Add your test code here
|
|
80
|
+
console.log('Tests not yet implemented');
|
|
81
|
+
language: javascript
|
|
82
|
+
readonly: false
|
|
83
|
+
task: "Implement the tests in userService.test.js. Use assert.strictEqual (or similar) to verify getUserById, getAllUsers, and createUser. Run node userService.test.js—it should print that tests passed."
|
|
84
|
+
validations:
|
|
85
|
+
- label: "Tests pass and userService.test.js was modified"
|
|
86
|
+
check: file-changed
|
|
87
|
+
pattern: "userService.test.js"
|
|
88
|
+
- label: "Output indicates passing tests"
|
|
89
|
+
check: output-contains
|
|
90
|
+
pattern: "pass"
|
|
91
|
+
hints:
|
|
92
|
+
- "Call getUserById(1) and assert the returned object has name 'Alice'."
|
|
93
|
+
- "getAllUsers() returns an array. Assert its length is >= 2."
|
|
94
|
+
- "createUser returns the new user. Assert it has name and email. Run tests with node userService.test.js."
|
|
95
|
+
agent_prompts:
|
|
96
|
+
on_start: "What does the service do? If you were a developer integrating with it, what would you want to verify works?"
|
|
@@ -0,0 +1,127 @@
|
|
|
1
|
+
# TypeScript — Types That Catch Bugs Before Runtime
|
|
2
|
+
|
|
3
|
+
## What is TypeScript?
|
|
4
|
+
|
|
5
|
+
TypeScript is JavaScript with static typing. A compiler checks types at build time; invalid code fails before it runs.
|
|
6
|
+
|
|
7
|
+
**JavaScript (runtime error):**
|
|
8
|
+
```javascript
|
|
9
|
+
function greet(name) {
|
|
10
|
+
return name.toUpperCase();
|
|
11
|
+
}
|
|
12
|
+
greet(42); // Runtime: name.toUpperCase is not a function
|
|
13
|
+
```
|
|
14
|
+
|
|
15
|
+
**TypeScript (compile-time error):**
|
|
16
|
+
```typescript
|
|
17
|
+
function greet(name: string) {
|
|
18
|
+
return name.toUpperCase();
|
|
19
|
+
}
|
|
20
|
+
greet(42); // Error: Argument of type 'number' is not assignable to parameter of type 'string'
|
|
21
|
+
```
|
|
22
|
+
|
|
23
|
+
## Basic Types
|
|
24
|
+
|
|
25
|
+
```typescript
|
|
26
|
+
let n: number = 42;
|
|
27
|
+
let s: string = "hello";
|
|
28
|
+
let b: boolean = true;
|
|
29
|
+
let arr: number[] = [1, 2, 3];
|
|
30
|
+
let tuple: [string, number] = ["a", 1];
|
|
31
|
+
let nothing: void = undefined;
|
|
32
|
+
```
|
|
33
|
+
|
|
34
|
+
TypeScript infers types when you assign directly, so explicit annotations are often optional.
|
|
35
|
+
|
|
36
|
+
## Interfaces
|
|
37
|
+
|
|
38
|
+
Define object shapes:
|
|
39
|
+
|
|
40
|
+
```typescript
|
|
41
|
+
interface User {
|
|
42
|
+
id: number;
|
|
43
|
+
name: string;
|
|
44
|
+
email?: string; // optional
|
|
45
|
+
}
|
|
46
|
+
const u: User = { id: 1, name: "Alice" };
|
|
47
|
+
```
|
|
48
|
+
|
|
49
|
+
Interfaces support inheritance and declaration merging.
|
|
50
|
+
|
|
51
|
+
## Type Aliases
|
|
52
|
+
|
|
53
|
+
Alternative to interfaces for object shapes and unions:
|
|
54
|
+
|
|
55
|
+
```typescript
|
|
56
|
+
type ID = string | number;
|
|
57
|
+
type Point = { x: number; y: number };
|
|
58
|
+
```
|
|
59
|
+
|
|
60
|
+
## Unions
|
|
61
|
+
|
|
62
|
+
A value can be one of several types:
|
|
63
|
+
|
|
64
|
+
```typescript
|
|
65
|
+
type Status = "pending" | "success" | "error";
|
|
66
|
+
function handle(s: Status) { /* ... */ }
|
|
67
|
+
```
|
|
68
|
+
|
|
69
|
+
## Generics
|
|
70
|
+
|
|
71
|
+
Reusable types parameterized by other types:
|
|
72
|
+
|
|
73
|
+
```typescript
|
|
74
|
+
function identity<T>(x: T): T {
|
|
75
|
+
return x;
|
|
76
|
+
}
|
|
77
|
+
const n = identity(42); // T inferred as number
|
|
78
|
+
const s = identity("hi"); // T inferred as string
|
|
79
|
+
```
|
|
80
|
+
|
|
81
|
+
```typescript
|
|
82
|
+
interface Box<T> {
|
|
83
|
+
value: T;
|
|
84
|
+
}
|
|
85
|
+
const box: Box<number> = { value: 42 };
|
|
86
|
+
```
|
|
87
|
+
|
|
88
|
+
## Type Narrowing
|
|
89
|
+
|
|
90
|
+
Narrow types with conditionals so TypeScript knows the type in each branch:
|
|
91
|
+
|
|
92
|
+
```typescript
|
|
93
|
+
function padLeft(value: string | number): string {
|
|
94
|
+
if (typeof value === "number") {
|
|
95
|
+
return " ".repeat(value); // value is number here
|
|
96
|
+
}
|
|
97
|
+
return value; // value is string here
|
|
98
|
+
}
|
|
99
|
+
```
|
|
100
|
+
|
|
101
|
+
Common narrowing: `typeof`, `instanceof`, `in`, discriminated unions.
|
|
102
|
+
|
|
103
|
+
## Utility Types
|
|
104
|
+
|
|
105
|
+
Built-in helpers:
|
|
106
|
+
|
|
107
|
+
| Type | Purpose |
|
|
108
|
+
|------|---------|
|
|
109
|
+
| `Partial<T>` | All properties optional |
|
|
110
|
+
| `Required<T>` | All properties required |
|
|
111
|
+
| `Pick<T, K>` | Select specific keys |
|
|
112
|
+
| `Omit<T, K>` | Exclude specific keys |
|
|
113
|
+
| `Record<K, V>` | Map keys to value type |
|
|
114
|
+
|
|
115
|
+
```typescript
|
|
116
|
+
type UserUpdate = Partial<User>; // All fields optional
|
|
117
|
+
type NameOnly = Pick<User, 'name'>;
|
|
118
|
+
```
|
|
119
|
+
|
|
120
|
+
## When Types Catch Bugs
|
|
121
|
+
|
|
122
|
+
| JS Bug | TS Catches It |
|
|
123
|
+
|--------|----------------|
|
|
124
|
+
| `undefined` property access | Strict null checks |
|
|
125
|
+
| Wrong argument type | Function parameter types |
|
|
126
|
+
| Typo in property name | Object shape checking |
|
|
127
|
+
| Missing required field | Interface compliance |
|
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
# TypeScript Exercises
|
|
2
|
+
|
|
3
|
+
## Exercise 1: Typed API Response
|
|
4
|
+
|
|
5
|
+
**Task:** Define an interface `ApiResponse<T>` with `data: T`, `status: number`, and `message?: string`. Create a `User` interface and type a variable as `ApiResponse<User>`.
|
|
6
|
+
|
|
7
|
+
**Validation:**
|
|
8
|
+
- [ ] ApiResponse is generic over the data shape
|
|
9
|
+
- [ ] status is required, message is optional
|
|
10
|
+
- [ ] Compiler catches missing or wrong-typed fields
|
|
11
|
+
|
|
12
|
+
**Hints:**
|
|
13
|
+
1. `interface ApiResponse<T> { data: T; ... }`
|
|
14
|
+
2. Use `?` for optional properties
|
|
15
|
+
3. `ApiResponse<User>` means T = User
|
|
16
|
+
|
|
17
|
+
---
|
|
18
|
+
|
|
19
|
+
## Exercise 2: Discriminated Union
|
|
20
|
+
|
|
21
|
+
**Task:** Create a type `Result` that is either `{ kind: 'ok'; value: number }` or `{ kind: 'error'; message: string }`. Write a function `unwrap(r: Result): number` that returns the value or throws the message.
|
|
22
|
+
|
|
23
|
+
**Validation:**
|
|
24
|
+
- [ ] Function uses `r.kind` to narrow
|
|
25
|
+
- [ ] In the 'ok' branch, `r.value` is accessible
|
|
26
|
+
- [ ] In the 'error' branch, `r.message` is accessible
|
|
27
|
+
|
|
28
|
+
**Hints:**
|
|
29
|
+
1. `type Result = OkResult | ErrorResult` with shared `kind`
|
|
30
|
+
2. `if (r.kind === 'ok') return r.value;`
|
|
31
|
+
3. Discriminated unions narrow on a common literal field
|
|
32
|
+
|
|
33
|
+
---
|
|
34
|
+
|
|
35
|
+
## Exercise 3: Generic Map Function
|
|
36
|
+
|
|
37
|
+
**Task:** Implement `map<T, U>(arr: T[], fn: (item: T) => U): U[]` — the standard array map. Ensure the types flow correctly from input to output.
|
|
38
|
+
|
|
39
|
+
**Validation:**
|
|
40
|
+
- [ ] `map([1,2,3], x => x * 2)` returns `number[]`
|
|
41
|
+
- [ ] `map(["a","b"], s => s.length)` returns `number[]`
|
|
42
|
+
- [ ] Wrong callback signature is rejected
|
|
43
|
+
|
|
44
|
+
**Hints:**
|
|
45
|
+
1. Two type parameters: T for input, U for output
|
|
46
|
+
2. `fn` takes `T` and returns `U`
|
|
47
|
+
3. Return type is `U[]`
|
|
48
|
+
|
|
49
|
+
---
|
|
50
|
+
|
|
51
|
+
## Exercise 4: Strict Null Safety
|
|
52
|
+
|
|
53
|
+
**Task:** Enable strict null checks (if not already). Write a function `getLength(s: string | null): number` that returns 0 for null and the length otherwise. Don't use non-null assertion (`!`).
|
|
54
|
+
|
|
55
|
+
**Validation:**
|
|
56
|
+
- [ ] `getLength(null)` returns 0
|
|
57
|
+
- [ ] `getLength("hi")` returns 2
|
|
58
|
+
- [ ] No `!` used; narrowing handles null
|
|
59
|
+
|
|
60
|
+
**Hints:**
|
|
61
|
+
1. `if (s === null) return 0;`
|
|
62
|
+
2. After the check, `s` is narrowed to string
|
|
63
|
+
3. Or use ternary: `s ? s.length : 0`
|
|
64
|
+
|
|
65
|
+
---
|
|
66
|
+
|
|
67
|
+
## Exercise 5: Pick and Omit
|
|
68
|
+
|
|
69
|
+
**Task:** Given `interface Config { apiUrl: string; timeout: number; secret: string }`, create a type `PublicConfig` that excludes `secret` using `Omit`. Create `TimeoutOnly` using `Pick` with just `timeout`.
|
|
70
|
+
|
|
71
|
+
**Validation:**
|
|
72
|
+
- [ ] PublicConfig has apiUrl and timeout, not secret
|
|
73
|
+
- [ ] TimeoutOnly has only timeout
|
|
74
|
+
- [ ] Uses built-in utility types
|
|
75
|
+
|
|
76
|
+
**Hints:**
|
|
77
|
+
1. `Omit<Config, 'secret'>`
|
|
78
|
+
2. `Pick<Config, 'timeout'>`
|
|
79
|
+
3. Can combine: `Pick<Omit<Config, 'secret'>, 'apiUrl'>`
|
|
@@ -0,0 +1,111 @@
|
|
|
1
|
+
games:
|
|
2
|
+
- type: speed-round
|
|
3
|
+
title: "Type This!"
|
|
4
|
+
rounds:
|
|
5
|
+
- question: "An array of strings. What's the type?"
|
|
6
|
+
options:
|
|
7
|
+
- "Array<string>"
|
|
8
|
+
- "string[]"
|
|
9
|
+
- "string Array"
|
|
10
|
+
- "[]string"
|
|
11
|
+
answer: 1
|
|
12
|
+
timeLimit: 12
|
|
13
|
+
- question: "A function that takes a number and returns a boolean. What's the type?"
|
|
14
|
+
options:
|
|
15
|
+
- "function number -> boolean"
|
|
16
|
+
- "(n: number) => boolean"
|
|
17
|
+
- "NumberBoolean"
|
|
18
|
+
- "Function<number, boolean>"
|
|
19
|
+
answer: 1
|
|
20
|
+
timeLimit: 16
|
|
21
|
+
- question: "An object with keys that can be any string, and values that are numbers. What's the type?"
|
|
22
|
+
options:
|
|
23
|
+
- "object"
|
|
24
|
+
- "Record<string, number>"
|
|
25
|
+
- "{ [key: string]: number }"
|
|
26
|
+
- "Map<string, number>"
|
|
27
|
+
answer: 1
|
|
28
|
+
timeLimit: 16
|
|
29
|
+
- question: "A value that can be either a string or null. What's the type?"
|
|
30
|
+
options:
|
|
31
|
+
- "string | null"
|
|
32
|
+
- "string | undefined"
|
|
33
|
+
- "string?"
|
|
34
|
+
- "optional string"
|
|
35
|
+
answer: 0
|
|
36
|
+
timeLimit: 14
|
|
37
|
+
- question: "A Promise that resolves to a User object. What's the type?"
|
|
38
|
+
options:
|
|
39
|
+
- "Promise User"
|
|
40
|
+
- "Promise<User>"
|
|
41
|
+
- "async User"
|
|
42
|
+
- "User | Promise"
|
|
43
|
+
answer: 1
|
|
44
|
+
timeLimit: 14
|
|
45
|
+
- question: "A tuple with exactly [string, number] in that order. What's the type?"
|
|
46
|
+
options:
|
|
47
|
+
- "Array<string | number>"
|
|
48
|
+
- "[string, number]"
|
|
49
|
+
- "tuple string number"
|
|
50
|
+
- "(string, number)"
|
|
51
|
+
answer: 1
|
|
52
|
+
timeLimit: 14
|
|
53
|
+
- question: "A function with no parameters that returns void. What's the type?"
|
|
54
|
+
options:
|
|
55
|
+
- "() => void"
|
|
56
|
+
- "void"
|
|
57
|
+
- "function void"
|
|
58
|
+
- "()void"
|
|
59
|
+
answer: 0
|
|
60
|
+
timeLimit: 12
|
|
61
|
+
- question: "A union of string literals: 'pending' | 'success' | 'error'. What's the type?"
|
|
62
|
+
options:
|
|
63
|
+
- "string"
|
|
64
|
+
- "'pending' | 'success' | 'error'"
|
|
65
|
+
- "enum Status"
|
|
66
|
+
- "Status"
|
|
67
|
+
answer: 1
|
|
68
|
+
timeLimit: 16
|
|
69
|
+
- question: "A generic type that wraps any type T. What's the type for a box containing a number?"
|
|
70
|
+
options:
|
|
71
|
+
- "Box number"
|
|
72
|
+
- "Box<number>"
|
|
73
|
+
- "Generic<number>"
|
|
74
|
+
- "T<number>"
|
|
75
|
+
answer: 1
|
|
76
|
+
timeLimit: 14
|
|
77
|
+
- question: "A readonly array that cannot be mutated. What's the type?"
|
|
78
|
+
options:
|
|
79
|
+
- "array"
|
|
80
|
+
- "ReadonlyArray<string>"
|
|
81
|
+
- "const string[]"
|
|
82
|
+
- "immutable string[]"
|
|
83
|
+
answer: 1
|
|
84
|
+
timeLimit: 16
|
|
85
|
+
|
|
86
|
+
- type: classify
|
|
87
|
+
title: "Type vs Interface"
|
|
88
|
+
categories:
|
|
89
|
+
- name: "type alias"
|
|
90
|
+
color: "#58a6ff"
|
|
91
|
+
- name: "interface"
|
|
92
|
+
color: "#3fb950"
|
|
93
|
+
- name: "either works"
|
|
94
|
+
color: "#8b949e"
|
|
95
|
+
items:
|
|
96
|
+
- text: "A union type: string | number."
|
|
97
|
+
category: "type alias"
|
|
98
|
+
- text: "An object shape that will be extended by other types (extends)."
|
|
99
|
+
category: "interface"
|
|
100
|
+
- text: "A simple object shape with a few properties, no extension planned."
|
|
101
|
+
category: "either works"
|
|
102
|
+
- text: "A mapped type: { [K in keyof T]: T[K] }."
|
|
103
|
+
category: "type alias"
|
|
104
|
+
- text: "Declaration merging needed (e.g. augmenting a library's types)."
|
|
105
|
+
category: "interface"
|
|
106
|
+
- text: "A primitive alias: type ID = string."
|
|
107
|
+
category: "type alias"
|
|
108
|
+
- text: "An intersection type: A & B."
|
|
109
|
+
category: "type alias"
|
|
110
|
+
- text: "A class-implementable contract (class Foo implements Bar)."
|
|
111
|
+
category: "either works"
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
slug: typescript-fundamentals
|
|
2
|
+
title: "TypeScript — Types That Catch Bugs Before Runtime"
|
|
3
|
+
version: 1.0.0
|
|
4
|
+
description: "Add static typing to JavaScript — interfaces, generics, and type narrowing."
|
|
5
|
+
category: languages
|
|
6
|
+
tags: [typescript, javascript, types, static-analysis, generics]
|
|
7
|
+
difficulty: beginner
|
|
8
|
+
|
|
9
|
+
xp:
|
|
10
|
+
read: 10
|
|
11
|
+
walkthrough: 30
|
|
12
|
+
exercise: 20
|
|
13
|
+
quiz: 15
|
|
14
|
+
quiz-perfect-bonus: 10
|
|
15
|
+
game: 20
|
|
16
|
+
game-perfect-bonus: 10
|
|
17
|
+
|
|
18
|
+
time:
|
|
19
|
+
quick: 5
|
|
20
|
+
read: 15
|
|
21
|
+
guided: 45
|
|
22
|
+
|
|
23
|
+
prerequisites: []
|
|
24
|
+
related: [react-fundamentals, clean-code]
|
|
25
|
+
|
|
26
|
+
triggers:
|
|
27
|
+
- "What is TypeScript?"
|
|
28
|
+
- "How do I add types to JavaScript?"
|
|
29
|
+
- "What are generics in TypeScript?"
|
|
30
|
+
- "How do interfaces work in TypeScript?"
|
|
31
|
+
|
|
32
|
+
visuals:
|
|
33
|
+
diagrams: [diagram-mermaid]
|
|
34
|
+
quiz-types: [quiz-fill-blank, quiz-timed-choice]
|
|
35
|
+
game-types: [speed-round, classify]
|
|
36
|
+
playground: javascript
|
|
37
|
+
web-embeds: true
|
|
38
|
+
|
|
39
|
+
sources:
|
|
40
|
+
- url: "https://www.typescriptlang.org/docs/"
|
|
41
|
+
label: "TypeScript Docs"
|
|
42
|
+
type: docs
|
|
43
|
+
- url: "https://www.typescriptlang.org/play"
|
|
44
|
+
label: "TypeScript Playground"
|
|
45
|
+
type: docs
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
# TypeScript Quick Reference
|
|
2
|
+
|
|
3
|
+
## Basic Types
|
|
4
|
+
|
|
5
|
+
| Syntax | Meaning |
|
|
6
|
+
|--------|---------|
|
|
7
|
+
| `string`, `number`, `boolean` | Primitives |
|
|
8
|
+
| `string[]`, `Array<number>` | Arrays |
|
|
9
|
+
| `[string, number]` | Tuple |
|
|
10
|
+
| `string \| number` | Union |
|
|
11
|
+
| `?` on property | Optional |
|
|
12
|
+
| `T \| null` | Nullable |
|
|
13
|
+
|
|
14
|
+
## Interfaces vs Types
|
|
15
|
+
|
|
16
|
+
| | interface | type |
|
|
17
|
+
|---|-----------|------|
|
|
18
|
+
| Extends | `extends` | `&` (intersection) |
|
|
19
|
+
| Union | No | `A \| B` |
|
|
20
|
+
| Declaration merge | Yes | No |
|
|
21
|
+
|
|
22
|
+
## Generics
|
|
23
|
+
|
|
24
|
+
```typescript
|
|
25
|
+
function fn<T>(x: T): T { return x; }
|
|
26
|
+
type Box<T> = { value: T };
|
|
27
|
+
```
|
|
28
|
+
|
|
29
|
+
## Narrowing
|
|
30
|
+
|
|
31
|
+
| Guard | Example |
|
|
32
|
+
|-------|---------|
|
|
33
|
+
| `typeof` | `typeof x === 'string'` |
|
|
34
|
+
| `instanceof` | `x instanceof Date` |
|
|
35
|
+
| `in` | `'key' in obj` |
|
|
36
|
+
| Discriminated union | `obj.kind === 'ok'` |
|
|
37
|
+
|
|
38
|
+
## Utility Types
|
|
39
|
+
|
|
40
|
+
| Type | Result |
|
|
41
|
+
|------|--------|
|
|
42
|
+
| `Partial<T>` | All optional |
|
|
43
|
+
| `Required<T>` | All required |
|
|
44
|
+
| `Pick<T, K>` | Only K keys |
|
|
45
|
+
| `Omit<T, K>` | Exclude K keys |
|
|
46
|
+
| `Record<K, V>` | Map K → V |
|
|
47
|
+
|
|
48
|
+
## Decision: When to Add Types
|
|
49
|
+
|
|
50
|
+
| Scenario | Approach |
|
|
51
|
+
|----------|----------|
|
|
52
|
+
| Function params/return | Explicit or inferred |
|
|
53
|
+
| Object shapes | Interface or type |
|
|
54
|
+
| Reusable structures | Generics |
|
|
55
|
+
| External API | Declare module or use `unknown` |
|