@booklib/skills 1.3.2 → 1.4.0
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/AGENTS.md +108 -0
- package/CLAUDE.md +57 -0
- package/CODE_OF_CONDUCT.md +31 -0
- package/CONTRIBUTING.md +13 -0
- package/README.md +68 -45
- package/SECURITY.md +9 -0
- package/assets/logo.svg +36 -0
- package/demo.gif +0 -0
- package/demo.tape +40 -0
- package/docs/index.html +187 -0
- package/package.json +2 -2
- package/skills/effective-typescript/SKILL.md +166 -0
- package/skills/effective-typescript/evals/evals.json +36 -0
- package/skills/effective-typescript/examples/after.md +70 -0
- package/skills/effective-typescript/examples/before.md +47 -0
- package/skills/effective-typescript/references/api_reference.md +118 -0
- package/skills/effective-typescript/references/practices-catalog.md +371 -0
- package/skills/programming-with-rust/SKILL.md +194 -0
- package/skills/programming-with-rust/evals/evals.json +37 -0
- package/skills/programming-with-rust/examples/after.md +107 -0
- package/skills/programming-with-rust/examples/before.md +59 -0
- package/skills/programming-with-rust/references/api_reference.md +152 -0
- package/skills/programming-with-rust/references/practices-catalog.md +335 -0
- package/skills/rust-in-action/SKILL.md +290 -0
- package/skills/rust-in-action/evals/evals.json +38 -0
- package/skills/rust-in-action/examples/after.md +156 -0
- package/skills/rust-in-action/examples/before.md +56 -0
- package/skills/rust-in-action/references/practices-catalog.md +346 -0
- package/skills/rust-in-action/scripts/review.py +147 -0
- package/skills/skill-router/SKILL.md +16 -13
- package/skills/skill-router/references/skill-catalog.md +19 -1
package/docs/index.html
ADDED
|
@@ -0,0 +1,187 @@
|
|
|
1
|
+
<!DOCTYPE html>
|
|
2
|
+
<html lang="en">
|
|
3
|
+
<head>
|
|
4
|
+
<meta charset="UTF-8"/>
|
|
5
|
+
<meta name="viewport" content="width=device-width, initial-scale=1.0"/>
|
|
6
|
+
<title>booklib-ai/skills — Book-grounded AI agent skills</title>
|
|
7
|
+
<meta name="description" content="18 AI agent skills grounded in canonical programming books. Give your AI coding assistant expert knowledge from Clean Code, Effective Java, DDD, and more."/>
|
|
8
|
+
<meta property="og:title" content="booklib-ai/skills"/>
|
|
9
|
+
<meta property="og:description" content="Book-grounded AI agent skills for Claude Code, Cursor, Copilot, and Windsurf."/>
|
|
10
|
+
<meta property="og:image" content="https://booklib-ai.github.io/skills/logo.png"/>
|
|
11
|
+
<style>
|
|
12
|
+
*, *::before, *::after { box-sizing: border-box; margin: 0; padding: 0; }
|
|
13
|
+
|
|
14
|
+
body {
|
|
15
|
+
background: #0d0d1a;
|
|
16
|
+
color: #e2e8f0;
|
|
17
|
+
font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif;
|
|
18
|
+
line-height: 1.6;
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
a { color: #818cf8; text-decoration: none; }
|
|
22
|
+
a:hover { text-decoration: underline; }
|
|
23
|
+
|
|
24
|
+
/* Hero */
|
|
25
|
+
.hero {
|
|
26
|
+
text-align: center;
|
|
27
|
+
padding: 80px 24px 64px;
|
|
28
|
+
max-width: 720px;
|
|
29
|
+
margin: 0 auto;
|
|
30
|
+
}
|
|
31
|
+
.hero img { width: 96px; height: 96px; margin-bottom: 24px; }
|
|
32
|
+
.hero h1 { font-size: 2.5rem; font-weight: 700; color: #f1f5f9; letter-spacing: -0.03em; }
|
|
33
|
+
.hero p {
|
|
34
|
+
font-size: 1.125rem;
|
|
35
|
+
color: #94a3b8;
|
|
36
|
+
margin-top: 16px;
|
|
37
|
+
max-width: 520px;
|
|
38
|
+
margin-left: auto;
|
|
39
|
+
margin-right: auto;
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
/* Install */
|
|
43
|
+
.install {
|
|
44
|
+
display: flex;
|
|
45
|
+
align-items: center;
|
|
46
|
+
justify-content: center;
|
|
47
|
+
gap: 12px;
|
|
48
|
+
margin-top: 36px;
|
|
49
|
+
flex-wrap: wrap;
|
|
50
|
+
}
|
|
51
|
+
.install-box {
|
|
52
|
+
background: #161625;
|
|
53
|
+
border: 1px solid #2d2d4a;
|
|
54
|
+
border-radius: 10px;
|
|
55
|
+
padding: 12px 20px;
|
|
56
|
+
font-family: "SF Mono", "Fira Code", monospace;
|
|
57
|
+
font-size: 0.9rem;
|
|
58
|
+
color: #a5b4fc;
|
|
59
|
+
cursor: pointer;
|
|
60
|
+
user-select: all;
|
|
61
|
+
}
|
|
62
|
+
.copy-btn {
|
|
63
|
+
background: #6366f1;
|
|
64
|
+
color: white;
|
|
65
|
+
border: none;
|
|
66
|
+
border-radius: 8px;
|
|
67
|
+
padding: 12px 20px;
|
|
68
|
+
font-size: 0.875rem;
|
|
69
|
+
font-weight: 600;
|
|
70
|
+
cursor: pointer;
|
|
71
|
+
transition: background 0.15s;
|
|
72
|
+
}
|
|
73
|
+
.copy-btn:hover { background: #4f46e5; }
|
|
74
|
+
|
|
75
|
+
/* Badges */
|
|
76
|
+
.badges {
|
|
77
|
+
display: flex;
|
|
78
|
+
justify-content: center;
|
|
79
|
+
gap: 8px;
|
|
80
|
+
margin-top: 28px;
|
|
81
|
+
flex-wrap: wrap;
|
|
82
|
+
}
|
|
83
|
+
.badges img { height: 20px; }
|
|
84
|
+
|
|
85
|
+
/* Skills grid */
|
|
86
|
+
.skills-section {
|
|
87
|
+
max-width: 900px;
|
|
88
|
+
margin: 64px auto;
|
|
89
|
+
padding: 0 24px;
|
|
90
|
+
}
|
|
91
|
+
.skills-section h2 {
|
|
92
|
+
font-size: 1.5rem;
|
|
93
|
+
font-weight: 700;
|
|
94
|
+
color: #f1f5f9;
|
|
95
|
+
margin-bottom: 24px;
|
|
96
|
+
text-align: center;
|
|
97
|
+
}
|
|
98
|
+
.skills-grid {
|
|
99
|
+
display: grid;
|
|
100
|
+
grid-template-columns: repeat(auto-fill, minmax(260px, 1fr));
|
|
101
|
+
gap: 16px;
|
|
102
|
+
}
|
|
103
|
+
.skill-card {
|
|
104
|
+
background: #11111f;
|
|
105
|
+
border: 1px solid #1e1e35;
|
|
106
|
+
border-radius: 12px;
|
|
107
|
+
padding: 20px;
|
|
108
|
+
transition: border-color 0.15s, transform 0.15s;
|
|
109
|
+
}
|
|
110
|
+
.skill-card:hover {
|
|
111
|
+
border-color: #6366f1;
|
|
112
|
+
transform: translateY(-2px);
|
|
113
|
+
}
|
|
114
|
+
.skill-card .icon { font-size: 1.5rem; margin-bottom: 10px; }
|
|
115
|
+
.skill-card h3 { font-size: 0.95rem; font-weight: 600; color: #e2e8f0; margin-bottom: 6px; }
|
|
116
|
+
.skill-card p { font-size: 0.825rem; color: #64748b; line-height: 1.5; }
|
|
117
|
+
|
|
118
|
+
/* Footer */
|
|
119
|
+
footer {
|
|
120
|
+
text-align: center;
|
|
121
|
+
padding: 48px 24px;
|
|
122
|
+
color: #475569;
|
|
123
|
+
font-size: 0.875rem;
|
|
124
|
+
border-top: 1px solid #1e1e35;
|
|
125
|
+
}
|
|
126
|
+
footer a { color: #6366f1; }
|
|
127
|
+
</style>
|
|
128
|
+
</head>
|
|
129
|
+
<body>
|
|
130
|
+
|
|
131
|
+
<section class="hero">
|
|
132
|
+
<img src="https://raw.githubusercontent.com/booklib-ai/skills/main/assets/logo.svg" alt="booklib-ai skills logo"/>
|
|
133
|
+
<h1>Skills</h1>
|
|
134
|
+
<p>Book-grounded AI agent skills — expert knowledge from canonical programming books, packaged for Claude Code, Cursor, Copilot, and Windsurf.</p>
|
|
135
|
+
|
|
136
|
+
<div class="install">
|
|
137
|
+
<div class="install-box" id="cmd">npx skills add booklib-ai/skills --all -g</div>
|
|
138
|
+
<button class="copy-btn" onclick="copy()">Copy</button>
|
|
139
|
+
</div>
|
|
140
|
+
|
|
141
|
+
<div class="badges">
|
|
142
|
+
<img src="https://img.shields.io/npm/v/@booklib/skills.svg" alt="npm version"/>
|
|
143
|
+
<img src="https://img.shields.io/npm/dw/@booklib/skills.svg" alt="downloads"/>
|
|
144
|
+
<img src="https://img.shields.io/github/stars/booklib-ai/skills?style=flat" alt="stars"/>
|
|
145
|
+
<img src="https://img.shields.io/badge/License-MIT-yellow.svg" alt="license"/>
|
|
146
|
+
</div>
|
|
147
|
+
</section>
|
|
148
|
+
|
|
149
|
+
<section class="skills-section">
|
|
150
|
+
<h2>18 skills, 18 books</h2>
|
|
151
|
+
<div class="skills-grid">
|
|
152
|
+
<div class="skill-card"><div class="icon">🎬</div><h3>animation-at-work</h3><p>Web animation principles from Rachel Nabors — motion perception, 12 animation principles, performance.</p></div>
|
|
153
|
+
<div class="skill-card"><div class="icon">🧹</div><h3>clean-code-reviewer</h3><p>Code review against Robert C. Martin's Clean Code — naming, functions, comments, classes.</p></div>
|
|
154
|
+
<div class="skill-card"><div class="icon">🗄️</div><h3>data-intensive-patterns</h3><p>Reliable, scalable systems from Martin Kleppmann — storage engines, replication, transactions.</p></div>
|
|
155
|
+
<div class="skill-card"><div class="icon">🔀</div><h3>data-pipelines</h3><p>Pipeline practices from James Densmore — ingestion, streaming, transformation, orchestration.</p></div>
|
|
156
|
+
<div class="skill-card"><div class="icon">🏗️</div><h3>design-patterns</h3><p>GoF design patterns from Head First Design Patterns — creational, structural, behavioral.</p></div>
|
|
157
|
+
<div class="skill-card"><div class="icon">🧩</div><h3>domain-driven-design</h3><p>Eric Evans' DDD — Aggregates, Value Objects, Bounded Contexts, Ubiquitous Language.</p></div>
|
|
158
|
+
<div class="skill-card"><div class="icon">☕</div><h3>effective-java</h3><p>Joshua Bloch's Effective Java — object creation, generics, enums, lambdas, concurrency.</p></div>
|
|
159
|
+
<div class="skill-card"><div class="icon">🛡️</div><h3>effective-kotlin</h3><p>Marcin Moskała's Effective Kotlin — safety, readability, reusability, abstraction.</p></div>
|
|
160
|
+
<div class="skill-card"><div class="icon">🐍</div><h3>effective-python</h3><p>Brett Slatkin's Effective Python — Pythonic thinking, functions, classes, concurrency.</p></div>
|
|
161
|
+
<div class="skill-card"><div class="icon">⚡</div><h3>kotlin-in-action</h3><p>Kotlin in Action — functions, classes, lambdas, nullability, coroutines, flows.</p></div>
|
|
162
|
+
<div class="skill-card"><div class="icon">🚀</div><h3>lean-startup</h3><p>Eric Ries' The Lean Startup — MVP testing, validated learning, Build-Measure-Learn.</p></div>
|
|
163
|
+
<div class="skill-card"><div class="icon">🔧</div><h3>microservices-patterns</h3><p>Chris Richardson — decomposition, sagas, API gateways, event sourcing, CQRS.</p></div>
|
|
164
|
+
<div class="skill-card"><div class="icon">🎨</div><h3>refactoring-ui</h3><p>Refactoring UI by Wathan & Schoger — visual hierarchy, layout, typography, color.</p></div>
|
|
165
|
+
<div class="skill-card"><div class="icon">🗺️</div><h3>skill-router</h3><p><strong>Meta-skill.</strong> Automatically selects the 1–2 most relevant skills for any task.</p></div>
|
|
166
|
+
<div class="skill-card"><div class="icon">📊</div><h3>storytelling-with-data</h3><p>Cole Nussbaumer Knaflic — effective visuals, decluttering, narrative structure.</p></div>
|
|
167
|
+
<div class="skill-card"><div class="icon">🏛️</div><h3>system-design-interview</h3><p>Alex Xu — scaling, estimation, load balancing, caching, sharding, real-world designs.</p></div>
|
|
168
|
+
<div class="skill-card"><div class="icon">🔄</div><h3>using-asyncio-python</h3><p>Caleb Hattingh — coroutines, event loop, tasks, signal handling, aiohttp.</p></div>
|
|
169
|
+
<div class="skill-card"><div class="icon">🕷️</div><h3>web-scraping-python</h3><p>Ryan Mitchell — BeautifulSoup, Scrapy, Selenium, data storage, anti-detection.</p></div>
|
|
170
|
+
</div>
|
|
171
|
+
</section>
|
|
172
|
+
|
|
173
|
+
<footer>
|
|
174
|
+
<p>MIT License · <a href="https://github.com/booklib-ai/skills">GitHub</a> · <a href="https://github.com/booklib-ai/skills/blob/main/CONTRIBUTING.md">Contributing</a> · <a href="https://github.com/booklib-ai/skills/blob/main/AGENTS.md">Agent Setup</a></p>
|
|
175
|
+
</footer>
|
|
176
|
+
|
|
177
|
+
<script>
|
|
178
|
+
function copy() {
|
|
179
|
+
navigator.clipboard.writeText(document.getElementById('cmd').textContent);
|
|
180
|
+
const btn = document.querySelector('.copy-btn');
|
|
181
|
+
btn.textContent = 'Copied!';
|
|
182
|
+
setTimeout(() => btn.textContent = 'Copy', 2000);
|
|
183
|
+
}
|
|
184
|
+
</script>
|
|
185
|
+
|
|
186
|
+
</body>
|
|
187
|
+
</html>
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@booklib/skills",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.4.0",
|
|
4
4
|
"description": "Book knowledge distilled into structured AI skills for Claude Code and other AI assistants",
|
|
5
5
|
"bin": {
|
|
6
6
|
"skills": "bin/skills.js"
|
|
@@ -13,7 +13,7 @@
|
|
|
13
13
|
"agent-skills"
|
|
14
14
|
],
|
|
15
15
|
"license": "MIT",
|
|
16
|
-
"homepage": "https://
|
|
16
|
+
"homepage": "https://booklib-ai.github.io/skills/",
|
|
17
17
|
"repository": {
|
|
18
18
|
"type": "git",
|
|
19
19
|
"url": "git+https://github.com/booklib-ai/skills.git"
|
|
@@ -0,0 +1,166 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: effective-typescript
|
|
3
|
+
description: >
|
|
4
|
+
Review existing TypeScript code and write new TypeScript following the 62 items from
|
|
5
|
+
"Effective TypeScript" by Dan Vanderkam. Use when writing TypeScript, reviewing TypeScript
|
|
6
|
+
code, working with type design, avoiding any, managing type declarations, or migrating
|
|
7
|
+
JavaScript to TypeScript. Trigger on: "TypeScript best practices", "type safety", "any",
|
|
8
|
+
"type assertions", "type design", "strict mode", "TypeScript review", "migrate to TypeScript".
|
|
9
|
+
---
|
|
10
|
+
|
|
11
|
+
# Effective TypeScript Skill
|
|
12
|
+
|
|
13
|
+
Apply the 62 items from Dan Vanderkam's "Effective TypeScript" to review existing code and write new TypeScript. This skill operates in two modes: **Review Mode** (analyze code for violations) and **Write Mode** (produce idiomatic, well-typed TypeScript from scratch).
|
|
14
|
+
|
|
15
|
+
## Reference Files
|
|
16
|
+
|
|
17
|
+
This skill includes categorized reference files covering all 62 items:
|
|
18
|
+
|
|
19
|
+
- `ref-01-getting-to-know-ts.md` — Items 1-5: TS/JS relationship, compiler options, code generation, structural typing, any
|
|
20
|
+
- `ref-02-type-system.md` — Items 6-18: editor, sets, type vs value space, declarations vs assertions, object wrappers, excess property checking, generics, readonly, mapped types
|
|
21
|
+
- `ref-03-type-inference.md` — Items 19-27: inferable types, widening, narrowing, objects at once, aliases, async/await, context, functional constructs
|
|
22
|
+
- `ref-04-type-design.md` — Items 28-37: valid states, Postel's Law, documentation, null perimeter, unions of interfaces, string types, branded types
|
|
23
|
+
- `ref-05-working-with-any.md` — Items 38-44: narrowest scope, precise any variants, unsafe assertions, evolving any, unknown, monkey patching, type coverage
|
|
24
|
+
- `ref-06-type-declarations.md` — Items 45-52: devDependencies, three versions, export types, TSDoc, this in callbacks, conditional types, mirror types, testing types
|
|
25
|
+
- `ref-07-writing-running-code.md` — Items 53-57: ECMAScript features, iterating objects, DOM hierarchy, private, source maps
|
|
26
|
+
- `ref-08-migrating.md` — Items 58-62: modern JS, @ts-check, allowJs, module-by-module, noImplicitAny
|
|
27
|
+
|
|
28
|
+
## How to Use This Skill
|
|
29
|
+
|
|
30
|
+
**Before responding**, read the relevant reference files based on the code's topic. For a general review, read all files. For targeted work (e.g., type design), read the specific reference (e.g., `ref-04-type-design.md`).
|
|
31
|
+
|
|
32
|
+
---
|
|
33
|
+
|
|
34
|
+
## Mode 1: Code Review
|
|
35
|
+
|
|
36
|
+
When the user asks you to **review** existing TypeScript code, follow this process:
|
|
37
|
+
|
|
38
|
+
### Step 1: Read Relevant References
|
|
39
|
+
Determine which chapters apply to the code under review and read those reference files. If unsure, read all of them.
|
|
40
|
+
|
|
41
|
+
### Step 2: Analyze the Code
|
|
42
|
+
For each relevant item from the book, check whether the code follows or violates the guideline. Focus on:
|
|
43
|
+
|
|
44
|
+
1. **TypeScript Fundamentals** (Items 1-5): Is `strict` mode enabled? Is `any` used carelessly? Does structural typing cause surprises?
|
|
45
|
+
2. **Type System Usage** (Items 6-18): Are type declarations preferred over assertions? Are object wrapper types avoided? Are `readonly` and mapped types used appropriately?
|
|
46
|
+
3. **Type Inference** (Items 19-27): Is inference relied upon where possible? Are `async`/`await` used over callbacks? Are aliases consistent?
|
|
47
|
+
4. **Type Design** (Items 28-37): Do types represent only valid states? Are string types replaced with literal unions? Are null values pushed to the perimeter?
|
|
48
|
+
5. **Working with any** (Items 38-44): Is `any` scoped as narrowly as possible? Is `unknown` used for truly unknown values? Are unsafe assertions hidden in well-typed wrappers?
|
|
49
|
+
6. **Type Declarations** (Items 45-52): Are `@types` in devDependencies? Are public API types exported? Is TSDoc used for comments?
|
|
50
|
+
7. **Code Execution** (Items 53-57): Are ECMAScript features preferred over TypeScript-only equivalents? Is object iteration done safely?
|
|
51
|
+
8. **Migration** (Items 58-62): Is modern JavaScript used as a baseline? Is migration done module-by-module?
|
|
52
|
+
|
|
53
|
+
### Step 3: Report Findings
|
|
54
|
+
For each issue found, report:
|
|
55
|
+
- **Item number and name** (e.g., "Item 9: Prefer Type Declarations to Type Assertions")
|
|
56
|
+
- **Location** in the code
|
|
57
|
+
- **What's wrong** (the anti-pattern)
|
|
58
|
+
- **How to fix it** (the TypeScript-idiomatic way)
|
|
59
|
+
- **Priority**: Critical (bugs/correctness), Important (maintainability), Suggestion (style)
|
|
60
|
+
|
|
61
|
+
### Step 4: Provide Fixed Code
|
|
62
|
+
Offer a corrected version of the code with all issues addressed, with comments explaining each change.
|
|
63
|
+
|
|
64
|
+
---
|
|
65
|
+
|
|
66
|
+
## Mode 2: Writing New Code
|
|
67
|
+
|
|
68
|
+
When the user asks you to **write** new TypeScript code, apply these core practices:
|
|
69
|
+
|
|
70
|
+
### Always Apply These Core Practices
|
|
71
|
+
|
|
72
|
+
1. **Enable strict mode** (Item 2). Never write TypeScript without `"strict": true` in tsconfig.json.
|
|
73
|
+
|
|
74
|
+
2. **Prefer type declarations over assertions** (Item 9). Use `const x: MyType = value` not `const x = value as MyType`.
|
|
75
|
+
|
|
76
|
+
3. **Avoid object wrapper types** (Item 10). Use `string`, `number`, `boolean` — never `String`, `Number`, `Boolean`.
|
|
77
|
+
|
|
78
|
+
4. **Use types that represent only valid states** (Item 28). Eliminate impossible states at the type level with tagged unions.
|
|
79
|
+
|
|
80
|
+
5. **Push null to the perimeter** (Item 31). Don't scatter `T | null` throughout — handle nullability at boundaries.
|
|
81
|
+
|
|
82
|
+
6. **Prefer unions of interfaces to interfaces of unions** (Item 32). Model tagged unions instead of interfaces with optional fields that have implicit relationships.
|
|
83
|
+
|
|
84
|
+
7. **Replace plain string types with string literal unions** (Item 33). `type Direction = 'north' | 'south' | 'east' | 'west'` not `string`.
|
|
85
|
+
|
|
86
|
+
8. **Generate types from APIs and specs, not data** (Item 35). Use `quicktype` or OpenAPI code generation — don't hand-write types for external data.
|
|
87
|
+
|
|
88
|
+
9. **Use `unknown` instead of `any` for values with unknown type** (Item 42). `unknown` forces callers to narrow before use.
|
|
89
|
+
|
|
90
|
+
10. **Scope `any` as narrowly as possible** (Item 38). Apply it to a single value, never a whole object or module.
|
|
91
|
+
|
|
92
|
+
11. **Use `readonly` to prevent mutation bugs** (Item 17). Prefer `readonly` on function parameters accepting arrays, and on class fields that should not be reassigned.
|
|
93
|
+
|
|
94
|
+
12. **Use `async`/`await` over raw Promises and callbacks** (Item 25). It produces cleaner inferred types and clearer code.
|
|
95
|
+
|
|
96
|
+
13. **Use type aliases to avoid repeating yourself** (Item 14). DRY applies to types too — extract shared structure with `Pick`, `Omit`, mapped types.
|
|
97
|
+
|
|
98
|
+
14. **Export all types that appear in public APIs** (Item 47). Don't force users to reconstruct types with `ReturnType<>` or `Parameters<>`.
|
|
99
|
+
|
|
100
|
+
15. **Use TSDoc for API comments** (Item 48). `/** */` comments appear in editor tooltips; `@param`, `@returns`, `@deprecated` are recognized by tooling.
|
|
101
|
+
|
|
102
|
+
### Type Structure Template
|
|
103
|
+
|
|
104
|
+
```typescript
|
|
105
|
+
// Prefer interfaces for object shapes (extendable); type aliases for unions/intersections
|
|
106
|
+
interface User {
|
|
107
|
+
readonly id: UserId; // Item 17: readonly on fields that shouldn't change
|
|
108
|
+
name: string;
|
|
109
|
+
email: string;
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
// Branded type for nominal typing (Item 37)
|
|
113
|
+
type UserId = string & { readonly __brand: 'UserId' };
|
|
114
|
+
|
|
115
|
+
// Tagged union — only valid states representable (Item 28, 32)
|
|
116
|
+
type RequestState<T> =
|
|
117
|
+
| { status: 'loading' }
|
|
118
|
+
| { status: 'success'; data: T }
|
|
119
|
+
| { status: 'error'; message: string };
|
|
120
|
+
|
|
121
|
+
// unknown, not any, for values from external sources (Item 42)
|
|
122
|
+
function parseResponse(json: string): unknown {
|
|
123
|
+
return JSON.parse(json);
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
// async/await over callbacks (Item 25)
|
|
127
|
+
async function fetchUser(id: UserId): Promise<User> {
|
|
128
|
+
const response = await fetch(`/api/users/${id}`);
|
|
129
|
+
if (!response.ok) throw new Error(`HTTP ${response.status}`);
|
|
130
|
+
return response.json() as User; // narrowly scoped assertion inside well-typed function (Item 40)
|
|
131
|
+
}
|
|
132
|
+
```
|
|
133
|
+
|
|
134
|
+
### any Guidelines
|
|
135
|
+
- If `any` is unavoidable, apply it to the smallest possible scope (Item 38)
|
|
136
|
+
- Prefer `unknown` for values received from external sources (Item 42)
|
|
137
|
+
- Hide unsafe assertions inside well-typed wrapper functions (Item 40)
|
|
138
|
+
- Track type coverage with `type-coverage` CLI to prevent regressions (Item 44)
|
|
139
|
+
|
|
140
|
+
---
|
|
141
|
+
|
|
142
|
+
## Priority of Items by Impact
|
|
143
|
+
|
|
144
|
+
### Critical (Correctness & Bugs)
|
|
145
|
+
- Item 2: Enable `strict` mode — `noImplicitAny` and `strictNullChecks` prevent whole classes of bugs
|
|
146
|
+
- Item 9: Prefer declarations to assertions — assertions bypass the type checker
|
|
147
|
+
- Item 28: Types that always represent valid states — impossible states cause runtime errors
|
|
148
|
+
- Item 31: Push null to the perimeter — scattered nullability causes null dereferences
|
|
149
|
+
- Item 42: Use `unknown` instead of `any` — `any` silently disables type checking
|
|
150
|
+
|
|
151
|
+
### Important (Maintainability)
|
|
152
|
+
- Item 13: Know the differences between `type` and `interface`
|
|
153
|
+
- Item 14: Use type operations and generics to avoid repetition
|
|
154
|
+
- Item 17: Use `readonly` to prevent mutation bugs
|
|
155
|
+
- Item 25: Use `async`/`await` over callbacks
|
|
156
|
+
- Item 32: Prefer unions of interfaces to interfaces of unions
|
|
157
|
+
- Item 33: Prefer string literal unions over plain `string`
|
|
158
|
+
- Item 47: Export all types that appear in public APIs
|
|
159
|
+
- Item 48: Use TSDoc for API comments
|
|
160
|
+
|
|
161
|
+
### Suggestions (Polish & Optimization)
|
|
162
|
+
- Item 19: Omit inferable types to reduce clutter
|
|
163
|
+
- Item 35: Generate types from APIs and specs
|
|
164
|
+
- Item 37: Consider brands for nominal typing
|
|
165
|
+
- Item 44: Track type coverage
|
|
166
|
+
- Item 53: Prefer ECMAScript features over TypeScript-only equivalents
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
{
|
|
2
|
+
"evals": [
|
|
3
|
+
{
|
|
4
|
+
"id": "eval-01-any-assertions-string-types",
|
|
5
|
+
"prompt": "Review this TypeScript code:\n\n```typescript\nasync function loadConfig(env: string): Promise<any> {\n const res = await fetch(`/config/${env}`);\n const data = res.json() as any;\n return data;\n}\n\ninterface AppState {\n loading: boolean;\n result?: any;\n error?: string;\n}\n\nfunction applyTheme(theme: string) {\n document.body.className = theme;\n}\n```",
|
|
6
|
+
"expectations": [
|
|
7
|
+
"Flag Item 42: return type should be unknown or a typed interface, not any",
|
|
8
|
+
"Flag Item 9: res.json() as any is a type assertion — prefer a type declaration or typed wrapper",
|
|
9
|
+
"Flag Item 28/32: AppState is an interface of unions — loading:true with result/error present is representable; suggest a tagged union instead",
|
|
10
|
+
"Flag Item 33: theme parameter should be a string literal union (e.g. 'light' | 'dark'), not plain string",
|
|
11
|
+
"Provide a fixed version using unknown, a tagged union, and a literal union type"
|
|
12
|
+
]
|
|
13
|
+
},
|
|
14
|
+
{
|
|
15
|
+
"id": "eval-02-subtle-interface-of-unions",
|
|
16
|
+
"prompt": "Review this TypeScript code:\n\n```typescript\ninterface FetchState {\n isLoading: boolean;\n data?: User[];\n errorMessage?: string;\n lastUpdated?: Date;\n}\n\nfunction render(state: FetchState) {\n if (state.isLoading) {\n showSpinner();\n } else if (state.errorMessage) {\n showError(state.errorMessage);\n } else {\n showData(state.data!); // non-null assertion\n }\n}\n```",
|
|
17
|
+
"expectations": [
|
|
18
|
+
"Flag Item 32: FetchState is an interface of unions — data and errorMessage have an implicit relationship that the type doesn't enforce",
|
|
19
|
+
"Flag Item 28: states like { isLoading: false, data: [...], errorMessage: 'oops' } are representable but invalid",
|
|
20
|
+
"Flag the non-null assertion (!) on state.data — it's a symptom of the type not representing valid states",
|
|
21
|
+
"Suggest a tagged union: type FetchState = { status: 'loading' } | { status: 'success'; data: User[]; lastUpdated: Date } | { status: 'error'; message: string }",
|
|
22
|
+
"Show how the fixed type makes the render function exhaustive and removes the need for the non-null assertion"
|
|
23
|
+
]
|
|
24
|
+
},
|
|
25
|
+
{
|
|
26
|
+
"id": "eval-03-already-well-typed",
|
|
27
|
+
"prompt": "Review this TypeScript code:\n\n```typescript\ntype OrderId = string & { readonly __brand: 'OrderId' };\n\ntype OrderStatus = 'pending' | 'processing' | 'shipped' | 'delivered' | 'cancelled';\n\ntype OrderResult =\n | { status: 'success'; order: Order }\n | { status: 'not_found' }\n | { status: 'error'; message: string };\n\ninterface Order {\n readonly id: OrderId;\n readonly customerId: string;\n status: OrderStatus;\n items: readonly OrderItem[];\n}\n\n/**\n * Fetches an order by ID.\n * @param id - The branded order identifier\n * @returns The order result discriminated union\n */\nasync function fetchOrder(id: OrderId): Promise<OrderResult> {\n const response = await fetch(`/api/orders/${id}`);\n if (response.status === 404) return { status: 'not_found' };\n if (!response.ok) return { status: 'error', message: `HTTP ${response.status}` };\n const raw: unknown = await response.json();\n return { status: 'success', order: raw as Order };\n}\n```",
|
|
28
|
+
"expectations": [
|
|
29
|
+
"Recognize that this code is already applying Effective TypeScript principles correctly",
|
|
30
|
+
"Acknowledge Item 37 (branded OrderId), Item 33 (OrderStatus literal union), Item 28/32 (tagged union OrderResult), Item 17 (readonly fields), Item 42 (unknown from JSON), Item 40 (assertion inside well-typed function), Item 25 (async/await), Item 48 (TSDoc)",
|
|
31
|
+
"Do NOT manufacture issues — the code is well-typed",
|
|
32
|
+
"At most offer minor suggestions, clearly marked as optional polish"
|
|
33
|
+
]
|
|
34
|
+
}
|
|
35
|
+
]
|
|
36
|
+
}
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
# After: Effective TypeScript
|
|
2
|
+
|
|
3
|
+
The same API client rewritten applying Effective TypeScript principles.
|
|
4
|
+
|
|
5
|
+
```typescript
|
|
6
|
+
// tsconfig.json has "strict": true (Item 2)
|
|
7
|
+
|
|
8
|
+
// Extract repeated shape into a named type (Item 14)
|
|
9
|
+
interface User {
|
|
10
|
+
readonly id: UserId; // Item 17: readonly on identity fields
|
|
11
|
+
name: string;
|
|
12
|
+
email: string;
|
|
13
|
+
role: 'admin' | 'viewer' | 'editor'; // Item 33: literal union, not plain string
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
// Branded type prevents passing a raw string where a UserId is expected (Item 37)
|
|
17
|
+
type UserId = string & { readonly __brand: 'UserId' };
|
|
18
|
+
|
|
19
|
+
// Tagged union — only valid states are representable (Item 28, 32)
|
|
20
|
+
// Impossible: { status: 'loading', data: [...] } or { status: 'success', error: '...' }
|
|
21
|
+
type RequestState<T> =
|
|
22
|
+
| { status: 'loading' }
|
|
23
|
+
| { status: 'success'; data: T }
|
|
24
|
+
| { status: 'error'; message: string };
|
|
25
|
+
|
|
26
|
+
// Return unknown from untrusted sources — callers must narrow (Item 42)
|
|
27
|
+
async function getUser(id: UserId): Promise<User> {
|
|
28
|
+
const response = await fetch(`/api/users/${id}`);
|
|
29
|
+
if (!response.ok) throw new Error(`HTTP ${response.status}`);
|
|
30
|
+
const raw: unknown = await response.json();
|
|
31
|
+
// Unsafe assertion scoped inside well-typed function boundary (Item 40)
|
|
32
|
+
return raw as User;
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
// Type declaration, not assertion (Item 9)
|
|
36
|
+
const userIdInput: HTMLInputElement | null = document.getElementById('user-id') as HTMLInputElement | null;
|
|
37
|
+
|
|
38
|
+
// Null pushed to the perimeter — caller handles it once (Item 31)
|
|
39
|
+
function processUser(user: User): string {
|
|
40
|
+
return user.name.toUpperCase(); // safe — name: string, not string | null
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
// String literal union, not plain string (Item 33)
|
|
44
|
+
type Direction = 'north' | 'south' | 'east' | 'west';
|
|
45
|
+
function setDirection(direction: Direction) {
|
|
46
|
+
// TypeScript rejects "sideways" at compile time
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
// async/await over callbacks — types flow naturally (Item 25)
|
|
50
|
+
async function fetchProfile(id: UserId): Promise<User> {
|
|
51
|
+
const response = await fetch(`/api/profiles/${id}`);
|
|
52
|
+
if (!response.ok) throw new Error(`HTTP ${response.status}`);
|
|
53
|
+
return response.json() as User;
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
// Named type used in all three functions — DRY (Item 14)
|
|
57
|
+
function renderAdmin(user: User): void {}
|
|
58
|
+
function updateAdmin(user: User): void {}
|
|
59
|
+
function deleteAdmin(user: User): void {}
|
|
60
|
+
```
|
|
61
|
+
|
|
62
|
+
**Key improvements:**
|
|
63
|
+
- `strict: true` enables `noImplicitAny` and `strictNullChecks` (Item 2)
|
|
64
|
+
- `RequestState<T>` tagged union eliminates impossible states (Items 28, 32)
|
|
65
|
+
- `UserId` branded type prevents mixing up `string` IDs (Item 37)
|
|
66
|
+
- `role` is a literal union, not `string` (Item 33)
|
|
67
|
+
- `unknown` returned from untrusted JSON; unsafe assertion hidden inside typed boundary (Items 40, 42)
|
|
68
|
+
- `async`/`await` replaces callback (Item 25)
|
|
69
|
+
- `User` interface defined once and reused (Item 14)
|
|
70
|
+
- `readonly` on `id` prevents accidental reassignment (Item 17)
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
# Before: Effective TypeScript
|
|
2
|
+
|
|
3
|
+
An API client for a user management service — written without applying Effective TypeScript principles.
|
|
4
|
+
|
|
5
|
+
```typescript
|
|
6
|
+
// No strict mode, any used freely, type assertions everywhere
|
|
7
|
+
|
|
8
|
+
async function getUser(id: string): Promise<any> {
|
|
9
|
+
const response = await fetch(`/api/users/${id}`);
|
|
10
|
+
const data = await response.json();
|
|
11
|
+
return data;
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
function processUser(user: any) {
|
|
15
|
+
console.log(user.name.toUpperCase()); // no null check, will crash if name is null
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
// Interface of unions — impossible states are representable
|
|
19
|
+
interface RequestState {
|
|
20
|
+
loading: boolean;
|
|
21
|
+
data?: any[]; // present when loading is false AND succeeded
|
|
22
|
+
error?: string; // present when loading is false AND failed
|
|
23
|
+
// What does { loading: false, data: [...], error: "oops" } mean?
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
// Plain string types instead of literal unions
|
|
27
|
+
function setDirection(direction: string) {
|
|
28
|
+
// accepts "north", "sideways", "diagonal", anything
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
// Type assertion instead of declaration
|
|
32
|
+
const userId = document.getElementById('user-id') as HTMLInputElement;
|
|
33
|
+
const value = userId.value as string;
|
|
34
|
+
|
|
35
|
+
// Callback-based async
|
|
36
|
+
function fetchProfile(id: string, callback: (err: Error | null, data: any) => void) {
|
|
37
|
+
fetch(`/api/profiles/${id}`)
|
|
38
|
+
.then(res => res.json())
|
|
39
|
+
.then(data => callback(null, data))
|
|
40
|
+
.catch(err => callback(err, null));
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
// Repeated type shape — DRY violation
|
|
44
|
+
function renderAdmin(user: { id: string; name: string; email: string; role: string }) {}
|
|
45
|
+
function updateAdmin(user: { id: string; name: string; email: string; role: string }) {}
|
|
46
|
+
function deleteAdmin(user: { id: string; name: string; email: string; role: string }) {}
|
|
47
|
+
```
|
|
@@ -0,0 +1,118 @@
|
|
|
1
|
+
# Effective TypeScript — All 62 Items
|
|
2
|
+
|
|
3
|
+
All items from Dan Vanderkam's "Effective TypeScript" organized by chapter with priority levels.
|
|
4
|
+
|
|
5
|
+
## Chapter 1: Getting to Know TypeScript (Items 1–5)
|
|
6
|
+
|
|
7
|
+
| Item | Title | Priority |
|
|
8
|
+
|------|-------|----------|
|
|
9
|
+
| 1 | Understand the Relationship Between TypeScript and JavaScript | Important |
|
|
10
|
+
| 2 | Know Which TypeScript Options You're Using | **Critical** |
|
|
11
|
+
| 3 | Understand That Code Generation Is Independent of Types | Important |
|
|
12
|
+
| 4 | Get Comfortable with Structural Typing | Important |
|
|
13
|
+
| 5 | Limit Use of the `any` Type | **Critical** |
|
|
14
|
+
|
|
15
|
+
## Chapter 2: TypeScript's Type System (Items 6–18)
|
|
16
|
+
|
|
17
|
+
| Item | Title | Priority |
|
|
18
|
+
|------|-------|----------|
|
|
19
|
+
| 6 | Use Your Editor to Interrogate and Explore the Type System | Suggestion |
|
|
20
|
+
| 7 | Think of Types as Sets of Values | Important |
|
|
21
|
+
| 8 | Know How to Tell Whether a Symbol Is in the Type Space or Value Space | Important |
|
|
22
|
+
| 9 | Prefer Type Declarations to Type Assertions | **Critical** |
|
|
23
|
+
| 10 | Avoid Object Wrapper Types (String, Number, Boolean, Symbol, BigInt) | **Critical** |
|
|
24
|
+
| 11 | Recognize the Limits of Excess Property Checking | Important |
|
|
25
|
+
| 12 | Apply Types to Entire Function Expressions When Possible | Important |
|
|
26
|
+
| 13 | Know the Differences Between `type` and `interface` | Important |
|
|
27
|
+
| 14 | Use Type Operations and Generics to Avoid Repeating Yourself | Important |
|
|
28
|
+
| 15 | Use Index Signatures for Dynamic Data | Important |
|
|
29
|
+
| 16 | Prefer Arrays, Tuples, and ArrayLike to `number` Index Signatures | Suggestion |
|
|
30
|
+
| 17 | Use `readonly` to Avoid Errors Associated with Mutation | Important |
|
|
31
|
+
| 18 | Use Mapped Types to Keep Values in Sync | Important |
|
|
32
|
+
|
|
33
|
+
## Chapter 3: Type Inference (Items 19–27)
|
|
34
|
+
|
|
35
|
+
| Item | Title | Priority |
|
|
36
|
+
|------|-------|----------|
|
|
37
|
+
| 19 | Avoid Cluttering Your Code with Inferable Types | Suggestion |
|
|
38
|
+
| 20 | Use Different Variables for Different Types | Important |
|
|
39
|
+
| 21 | Understand Type Widening | Important |
|
|
40
|
+
| 22 | Understand Type Narrowing | **Critical** |
|
|
41
|
+
| 23 | Create Objects All at Once | Important |
|
|
42
|
+
| 24 | Be Consistent in Your Use of Aliases | Important |
|
|
43
|
+
| 25 | Use `async` Functions Instead of Callbacks for Asynchronous Code | Important |
|
|
44
|
+
| 26 | Understand How Context Is Used in Type Inference | Important |
|
|
45
|
+
| 27 | Use Functional Constructs and Libraries to Help Types Flow | Suggestion |
|
|
46
|
+
|
|
47
|
+
## Chapter 4: Type Design (Items 28–37)
|
|
48
|
+
|
|
49
|
+
| Item | Title | Priority |
|
|
50
|
+
|------|-------|----------|
|
|
51
|
+
| 28 | Prefer Types That Always Represent Valid States | **Critical** |
|
|
52
|
+
| 29 | Be Liberal in What You Accept and Strict in What You Produce | Important |
|
|
53
|
+
| 30 | Don't Repeat Type Information in Documentation | Important |
|
|
54
|
+
| 31 | Push Null Values to the Perimeter of Your Types | **Critical** |
|
|
55
|
+
| 32 | Prefer Unions of Interfaces to Interfaces of Unions | **Critical** |
|
|
56
|
+
| 33 | Prefer More Precise Alternatives to String Types | Important |
|
|
57
|
+
| 34 | Prefer Incomplete Types to Inaccurate Types | Important |
|
|
58
|
+
| 35 | Generate Types from APIs and Specs, Not Data | Suggestion |
|
|
59
|
+
| 36 | Name Types Using the Language of Your Problem Domain | Important |
|
|
60
|
+
| 37 | Consider "Brands" for Nominal Typing | Suggestion |
|
|
61
|
+
|
|
62
|
+
## Chapter 5: Working with any (Items 38–44)
|
|
63
|
+
|
|
64
|
+
| Item | Title | Priority |
|
|
65
|
+
|------|-------|----------|
|
|
66
|
+
| 38 | Use the Narrowest Possible Scope for `any` Types | **Critical** |
|
|
67
|
+
| 39 | Prefer More Precise Variants of `any` to Plain `any` | Important |
|
|
68
|
+
| 40 | Hide Unsafe Type Assertions in Well-Typed Functions | Important |
|
|
69
|
+
| 41 | Understand Evolving `any` | Important |
|
|
70
|
+
| 42 | Use `unknown` Instead of `any` for Values with an Unknown Type | **Critical** |
|
|
71
|
+
| 43 | Prefer Type-Safe Approaches to Monkey Patching | Important |
|
|
72
|
+
| 44 | Track Your Type Coverage to Prevent Regressions in Type Safety | Suggestion |
|
|
73
|
+
|
|
74
|
+
## Chapter 6: Type Declarations and @types (Items 45–52)
|
|
75
|
+
|
|
76
|
+
| Item | Title | Priority |
|
|
77
|
+
|------|-------|----------|
|
|
78
|
+
| 45 | Put TypeScript and `@types` in devDependencies | Important |
|
|
79
|
+
| 46 | Understand the Three Versions Involved in Type Declarations | Important |
|
|
80
|
+
| 47 | Export All Types That Appear in Public APIs | Important |
|
|
81
|
+
| 48 | Use TSDoc for API Comments | Important |
|
|
82
|
+
| 49 | Provide a Type for `this` in Callbacks | Important |
|
|
83
|
+
| 50 | Prefer Conditional Types to Overloaded Declarations | Suggestion |
|
|
84
|
+
| 51 | Mirror Types to Sever Dependencies | Suggestion |
|
|
85
|
+
| 52 | Be Aware of the Pitfalls of Testing Types | Important |
|
|
86
|
+
|
|
87
|
+
## Chapter 7: Writing and Running Your Code (Items 53–57)
|
|
88
|
+
|
|
89
|
+
| Item | Title | Priority |
|
|
90
|
+
|------|-------|----------|
|
|
91
|
+
| 53 | Prefer ECMAScript Features to TypeScript Features | Important |
|
|
92
|
+
| 54 | Know How to Iterate Over Objects | Important |
|
|
93
|
+
| 55 | Understand the DOM Hierarchy | Important |
|
|
94
|
+
| 56 | Don't Rely on `private` to Hide Information | Important |
|
|
95
|
+
| 57 | Use Source Maps to Debug TypeScript | Suggestion |
|
|
96
|
+
|
|
97
|
+
## Chapter 8: Migrating to TypeScript (Items 58–62)
|
|
98
|
+
|
|
99
|
+
| Item | Title | Priority |
|
|
100
|
+
|------|-------|----------|
|
|
101
|
+
| 58 | Write Modern JavaScript | Important |
|
|
102
|
+
| 59 | Use `@ts-check` and JSDoc to Experiment with TypeScript | Suggestion |
|
|
103
|
+
| 60 | Use `allowJs` to Mix TypeScript and JavaScript | Important |
|
|
104
|
+
| 61 | Convert Module by Module Up Your Dependency Graph | Important |
|
|
105
|
+
| 62 | Don't Consider Migration Complete Until You Enable `noImplicitAny` | **Critical** |
|
|
106
|
+
|
|
107
|
+
---
|
|
108
|
+
|
|
109
|
+
## Priority Summary
|
|
110
|
+
|
|
111
|
+
**Critical (fix immediately — correctness or safety)**
|
|
112
|
+
Items: 2, 5, 9, 10, 22, 28, 31, 32, 38, 42, 62
|
|
113
|
+
|
|
114
|
+
**Important (fix soon — maintainability and idiom)**
|
|
115
|
+
Items: 3, 4, 7, 8, 11, 12, 13, 14, 15, 17, 18, 20, 21, 23, 24, 25, 26, 29, 30, 33, 34, 36, 39, 40, 41, 43, 45, 46, 47, 48, 49, 52, 53, 54, 55, 56, 58, 60, 61
|
|
116
|
+
|
|
117
|
+
**Suggestion (polish when time allows)**
|
|
118
|
+
Items: 6, 16, 19, 27, 35, 37, 44, 50, 51, 57, 59
|