@booklib/core 2.0.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/.cursor/rules/booklib-standards.mdc +40 -0
- package/.gemini/context.md +372 -0
- package/AGENTS.md +166 -0
- package/CHANGELOG.md +226 -0
- package/CLAUDE.md +81 -0
- package/CODE_OF_CONDUCT.md +31 -0
- package/CONTRIBUTING.md +304 -0
- package/LICENSE +21 -0
- package/PLAN.md +28 -0
- package/README.ja.md +198 -0
- package/README.ko.md +198 -0
- package/README.md +503 -0
- package/README.pt-BR.md +198 -0
- package/README.uk.md +241 -0
- package/README.zh-CN.md +198 -0
- package/SECURITY.md +9 -0
- package/agents/architecture-reviewer.md +136 -0
- package/agents/booklib-reviewer.md +90 -0
- package/agents/data-reviewer.md +107 -0
- package/agents/jvm-reviewer.md +146 -0
- package/agents/python-reviewer.md +128 -0
- package/agents/rust-reviewer.md +115 -0
- package/agents/ts-reviewer.md +110 -0
- package/agents/ui-reviewer.md +117 -0
- package/assets/logo.svg +36 -0
- package/bin/booklib-mcp.js +304 -0
- package/bin/booklib.js +1705 -0
- package/bin/skills.cjs +1292 -0
- package/booklib-router.mdc +36 -0
- package/booklib.config.json +19 -0
- package/commands/animation-at-work.md +10 -0
- package/commands/clean-code-reviewer.md +10 -0
- package/commands/data-intensive-patterns.md +10 -0
- package/commands/data-pipelines.md +10 -0
- package/commands/design-patterns.md +10 -0
- package/commands/domain-driven-design.md +10 -0
- package/commands/effective-java.md +10 -0
- package/commands/effective-kotlin.md +10 -0
- package/commands/effective-python.md +10 -0
- package/commands/effective-typescript.md +10 -0
- package/commands/kotlin-in-action.md +10 -0
- package/commands/lean-startup.md +10 -0
- package/commands/microservices-patterns.md +10 -0
- package/commands/programming-with-rust.md +10 -0
- package/commands/refactoring-ui.md +10 -0
- package/commands/rust-in-action.md +10 -0
- package/commands/skill-router.md +10 -0
- package/commands/spring-boot-in-action.md +10 -0
- package/commands/storytelling-with-data.md +10 -0
- package/commands/system-design-interview.md +10 -0
- package/commands/using-asyncio-python.md +10 -0
- package/commands/web-scraping-python.md +10 -0
- package/community/registry.json +1616 -0
- package/hooks/hooks.json +23 -0
- package/hooks/posttooluse-capture.mjs +67 -0
- package/hooks/suggest.js +153 -0
- package/lib/agent-behaviors.js +40 -0
- package/lib/agent-detector.js +96 -0
- package/lib/config-loader.js +39 -0
- package/lib/conflict-resolver.js +148 -0
- package/lib/context-builder.js +574 -0
- package/lib/discovery-engine.js +298 -0
- package/lib/doctor/hook-installer.js +83 -0
- package/lib/doctor/usage-tracker.js +87 -0
- package/lib/engine/ai-features.js +253 -0
- package/lib/engine/auditor.js +103 -0
- package/lib/engine/bm25-index.js +178 -0
- package/lib/engine/capture.js +120 -0
- package/lib/engine/corrections.js +198 -0
- package/lib/engine/doctor.js +195 -0
- package/lib/engine/graph-injector.js +137 -0
- package/lib/engine/graph.js +161 -0
- package/lib/engine/handoff.js +405 -0
- package/lib/engine/indexer.js +242 -0
- package/lib/engine/parser.js +53 -0
- package/lib/engine/query-expander.js +42 -0
- package/lib/engine/reranker.js +40 -0
- package/lib/engine/rrf.js +59 -0
- package/lib/engine/scanner.js +151 -0
- package/lib/engine/searcher.js +139 -0
- package/lib/engine/session-coordinator.js +306 -0
- package/lib/engine/session-manager.js +429 -0
- package/lib/engine/synthesizer.js +70 -0
- package/lib/installer.js +70 -0
- package/lib/instinct-block.js +33 -0
- package/lib/mcp-config-writer.js +88 -0
- package/lib/paths.js +57 -0
- package/lib/profiles/design.md +19 -0
- package/lib/profiles/general.md +16 -0
- package/lib/profiles/research-analysis.md +22 -0
- package/lib/profiles/software-development.md +23 -0
- package/lib/profiles/writing-content.md +19 -0
- package/lib/project-initializer.js +916 -0
- package/lib/registry/skills.js +102 -0
- package/lib/registry-searcher.js +99 -0
- package/lib/rules/rules-manager.js +169 -0
- package/lib/skill-fetcher.js +333 -0
- package/lib/well-known-builder.js +70 -0
- package/lib/wizard/index.js +404 -0
- package/lib/wizard/integration-detector.js +41 -0
- package/lib/wizard/project-detector.js +100 -0
- package/lib/wizard/prompt.js +156 -0
- package/lib/wizard/registry-embeddings.js +107 -0
- package/lib/wizard/skill-recommender.js +69 -0
- package/llms-full.txt +254 -0
- package/llms.txt +70 -0
- package/package.json +45 -0
- package/research-reports/2026-04-01-current-architecture.md +160 -0
- package/research-reports/IDEAS.md +93 -0
- package/rules/common/clean-code.md +42 -0
- package/rules/java/effective-java.md +42 -0
- package/rules/kotlin/effective-kotlin.md +37 -0
- package/rules/python/effective-python.md +38 -0
- package/rules/rust/rust.md +37 -0
- package/rules/typescript/effective-typescript.md +42 -0
- package/scripts/gen-llms-full.mjs +36 -0
- package/scripts/gen-og.mjs +142 -0
- package/scripts/validate-frontmatter.js +25 -0
- package/skills/animation-at-work/SKILL.md +270 -0
- package/skills/animation-at-work/assets/example_asset.txt +1 -0
- package/skills/animation-at-work/evals/evals.json +44 -0
- package/skills/animation-at-work/evals/results.json +13 -0
- package/skills/animation-at-work/examples/after.md +64 -0
- package/skills/animation-at-work/examples/before.md +35 -0
- package/skills/animation-at-work/references/api_reference.md +369 -0
- package/skills/animation-at-work/references/review-checklist.md +79 -0
- package/skills/animation-at-work/scripts/audit_animations.py +295 -0
- package/skills/animation-at-work/scripts/example.py +1 -0
- package/skills/clean-code-reviewer/SKILL.md +444 -0
- package/skills/clean-code-reviewer/audit.json +35 -0
- package/skills/clean-code-reviewer/evals/evals.json +185 -0
- package/skills/clean-code-reviewer/evals/results.json +13 -0
- package/skills/clean-code-reviewer/examples/after.md +48 -0
- package/skills/clean-code-reviewer/examples/before.md +33 -0
- package/skills/clean-code-reviewer/references/api_reference.md +158 -0
- package/skills/clean-code-reviewer/references/practices-catalog.md +282 -0
- package/skills/clean-code-reviewer/references/review-checklist.md +254 -0
- package/skills/clean-code-reviewer/scripts/pre-review.py +206 -0
- package/skills/data-intensive-patterns/SKILL.md +267 -0
- package/skills/data-intensive-patterns/assets/example_asset.txt +1 -0
- package/skills/data-intensive-patterns/evals/evals.json +54 -0
- package/skills/data-intensive-patterns/evals/results.json +13 -0
- package/skills/data-intensive-patterns/examples/after.md +61 -0
- package/skills/data-intensive-patterns/examples/before.md +38 -0
- package/skills/data-intensive-patterns/references/api_reference.md +34 -0
- package/skills/data-intensive-patterns/references/patterns-catalog.md +551 -0
- package/skills/data-intensive-patterns/references/review-checklist.md +193 -0
- package/skills/data-intensive-patterns/scripts/adr.py +213 -0
- package/skills/data-intensive-patterns/scripts/example.py +1 -0
- package/skills/data-pipelines/SKILL.md +259 -0
- package/skills/data-pipelines/assets/example_asset.txt +1 -0
- package/skills/data-pipelines/evals/evals.json +45 -0
- package/skills/data-pipelines/evals/results.json +13 -0
- package/skills/data-pipelines/examples/after.md +97 -0
- package/skills/data-pipelines/examples/before.md +37 -0
- package/skills/data-pipelines/references/api_reference.md +301 -0
- package/skills/data-pipelines/references/review-checklist.md +181 -0
- package/skills/data-pipelines/scripts/example.py +1 -0
- package/skills/data-pipelines/scripts/new_pipeline.py +444 -0
- package/skills/design-patterns/SKILL.md +271 -0
- package/skills/design-patterns/assets/example_asset.txt +1 -0
- package/skills/design-patterns/evals/evals.json +46 -0
- package/skills/design-patterns/evals/results.json +13 -0
- package/skills/design-patterns/examples/after.md +52 -0
- package/skills/design-patterns/examples/before.md +29 -0
- package/skills/design-patterns/references/api_reference.md +1 -0
- package/skills/design-patterns/references/patterns-catalog.md +726 -0
- package/skills/design-patterns/references/review-checklist.md +173 -0
- package/skills/design-patterns/scripts/example.py +1 -0
- package/skills/design-patterns/scripts/scaffold.py +807 -0
- package/skills/domain-driven-design/SKILL.md +142 -0
- package/skills/domain-driven-design/assets/example_asset.txt +1 -0
- package/skills/domain-driven-design/evals/evals.json +48 -0
- package/skills/domain-driven-design/evals/results.json +13 -0
- package/skills/domain-driven-design/examples/after.md +80 -0
- package/skills/domain-driven-design/examples/before.md +43 -0
- package/skills/domain-driven-design/references/api_reference.md +1 -0
- package/skills/domain-driven-design/references/patterns-catalog.md +545 -0
- package/skills/domain-driven-design/references/review-checklist.md +158 -0
- package/skills/domain-driven-design/scripts/example.py +1 -0
- package/skills/domain-driven-design/scripts/scaffold.py +421 -0
- package/skills/effective-java/SKILL.md +227 -0
- package/skills/effective-java/assets/example_asset.txt +1 -0
- package/skills/effective-java/evals/evals.json +46 -0
- package/skills/effective-java/evals/results.json +13 -0
- package/skills/effective-java/examples/after.md +83 -0
- package/skills/effective-java/examples/before.md +37 -0
- package/skills/effective-java/references/api_reference.md +1 -0
- package/skills/effective-java/references/items-catalog.md +955 -0
- package/skills/effective-java/references/review-checklist.md +216 -0
- package/skills/effective-java/scripts/checkstyle_setup.py +211 -0
- package/skills/effective-java/scripts/example.py +1 -0
- package/skills/effective-kotlin/SKILL.md +271 -0
- package/skills/effective-kotlin/assets/example_asset.txt +1 -0
- package/skills/effective-kotlin/audit.json +29 -0
- package/skills/effective-kotlin/evals/evals.json +45 -0
- package/skills/effective-kotlin/evals/results.json +13 -0
- package/skills/effective-kotlin/examples/after.md +36 -0
- package/skills/effective-kotlin/examples/before.md +38 -0
- package/skills/effective-kotlin/references/api_reference.md +1 -0
- package/skills/effective-kotlin/references/practices-catalog.md +1228 -0
- package/skills/effective-kotlin/references/review-checklist.md +126 -0
- package/skills/effective-kotlin/scripts/example.py +1 -0
- package/skills/effective-python/SKILL.md +441 -0
- package/skills/effective-python/evals/evals.json +44 -0
- package/skills/effective-python/evals/results.json +13 -0
- package/skills/effective-python/examples/after.md +56 -0
- package/skills/effective-python/examples/before.md +40 -0
- package/skills/effective-python/ref-01-pythonic-thinking.md +202 -0
- package/skills/effective-python/ref-02-lists-and-dicts.md +146 -0
- package/skills/effective-python/ref-03-functions.md +186 -0
- package/skills/effective-python/ref-04-comprehensions-generators.md +211 -0
- package/skills/effective-python/ref-05-classes-interfaces.md +188 -0
- package/skills/effective-python/ref-06-metaclasses-attributes.md +209 -0
- package/skills/effective-python/ref-07-concurrency.md +213 -0
- package/skills/effective-python/ref-08-robustness-performance.md +248 -0
- package/skills/effective-python/ref-09-testing-debugging.md +253 -0
- package/skills/effective-python/ref-10-collaboration.md +175 -0
- package/skills/effective-python/references/api_reference.md +218 -0
- package/skills/effective-python/references/practices-catalog.md +483 -0
- package/skills/effective-python/references/review-checklist.md +190 -0
- package/skills/effective-python/scripts/lint.py +173 -0
- package/skills/effective-typescript/SKILL.md +262 -0
- package/skills/effective-typescript/audit.json +29 -0
- package/skills/effective-typescript/evals/evals.json +37 -0
- package/skills/effective-typescript/evals/results.json +13 -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/effective-typescript/scripts/review.py +169 -0
- package/skills/kotlin-in-action/SKILL.md +261 -0
- package/skills/kotlin-in-action/assets/example_asset.txt +1 -0
- package/skills/kotlin-in-action/evals/evals.json +43 -0
- package/skills/kotlin-in-action/evals/results.json +13 -0
- package/skills/kotlin-in-action/examples/after.md +53 -0
- package/skills/kotlin-in-action/examples/before.md +39 -0
- package/skills/kotlin-in-action/references/api_reference.md +1 -0
- package/skills/kotlin-in-action/references/practices-catalog.md +436 -0
- package/skills/kotlin-in-action/references/review-checklist.md +204 -0
- package/skills/kotlin-in-action/scripts/example.py +1 -0
- package/skills/kotlin-in-action/scripts/setup_detekt.py +224 -0
- package/skills/lean-startup/SKILL.md +160 -0
- package/skills/lean-startup/assets/example_asset.txt +1 -0
- package/skills/lean-startup/evals/evals.json +43 -0
- package/skills/lean-startup/evals/results.json +13 -0
- package/skills/lean-startup/examples/after.md +80 -0
- package/skills/lean-startup/examples/before.md +34 -0
- package/skills/lean-startup/references/api_reference.md +319 -0
- package/skills/lean-startup/references/review-checklist.md +137 -0
- package/skills/lean-startup/scripts/example.py +1 -0
- package/skills/lean-startup/scripts/new_experiment.py +286 -0
- package/skills/microservices-patterns/SKILL.md +384 -0
- package/skills/microservices-patterns/evals/evals.json +45 -0
- package/skills/microservices-patterns/evals/results.json +13 -0
- package/skills/microservices-patterns/examples/after.md +69 -0
- package/skills/microservices-patterns/examples/before.md +40 -0
- package/skills/microservices-patterns/references/patterns-catalog.md +391 -0
- package/skills/microservices-patterns/references/review-checklist.md +169 -0
- package/skills/microservices-patterns/scripts/new_service.py +583 -0
- package/skills/programming-with-rust/SKILL.md +209 -0
- package/skills/programming-with-rust/evals/evals.json +37 -0
- package/skills/programming-with-rust/evals/results.json +13 -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/programming-with-rust/scripts/review.py +142 -0
- package/skills/refactoring-ui/SKILL.md +362 -0
- package/skills/refactoring-ui/assets/example_asset.txt +1 -0
- package/skills/refactoring-ui/evals/evals.json +45 -0
- package/skills/refactoring-ui/evals/results.json +13 -0
- package/skills/refactoring-ui/examples/after.md +85 -0
- package/skills/refactoring-ui/examples/before.md +58 -0
- package/skills/refactoring-ui/references/api_reference.md +355 -0
- package/skills/refactoring-ui/references/review-checklist.md +114 -0
- package/skills/refactoring-ui/scripts/audit_css.py +250 -0
- package/skills/refactoring-ui/scripts/example.py +1 -0
- package/skills/rust-in-action/SKILL.md +350 -0
- package/skills/rust-in-action/evals/evals.json +38 -0
- package/skills/rust-in-action/evals/results.json +13 -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 +186 -0
- package/skills/skill-router/evals/evals.json +38 -0
- package/skills/skill-router/evals/results.json +13 -0
- package/skills/skill-router/examples/after.md +63 -0
- package/skills/skill-router/examples/before.md +39 -0
- package/skills/skill-router/references/api_reference.md +24 -0
- package/skills/skill-router/references/routing-heuristics.md +89 -0
- package/skills/skill-router/references/skill-catalog.md +174 -0
- package/skills/skill-router/scripts/route.py +266 -0
- package/skills/spring-boot-in-action/SKILL.md +340 -0
- package/skills/spring-boot-in-action/evals/evals.json +39 -0
- package/skills/spring-boot-in-action/evals/results.json +13 -0
- package/skills/spring-boot-in-action/examples/after.md +185 -0
- package/skills/spring-boot-in-action/examples/before.md +84 -0
- package/skills/spring-boot-in-action/references/practices-catalog.md +403 -0
- package/skills/spring-boot-in-action/scripts/review.py +184 -0
- package/skills/storytelling-with-data/SKILL.md +241 -0
- package/skills/storytelling-with-data/assets/example_asset.txt +1 -0
- package/skills/storytelling-with-data/evals/evals.json +47 -0
- package/skills/storytelling-with-data/evals/results.json +13 -0
- package/skills/storytelling-with-data/examples/after.md +50 -0
- package/skills/storytelling-with-data/examples/before.md +33 -0
- package/skills/storytelling-with-data/references/api_reference.md +379 -0
- package/skills/storytelling-with-data/references/review-checklist.md +111 -0
- package/skills/storytelling-with-data/scripts/chart_review.py +301 -0
- package/skills/storytelling-with-data/scripts/example.py +1 -0
- package/skills/system-design-interview/SKILL.md +233 -0
- package/skills/system-design-interview/assets/example_asset.txt +1 -0
- package/skills/system-design-interview/evals/evals.json +46 -0
- package/skills/system-design-interview/evals/results.json +13 -0
- package/skills/system-design-interview/examples/after.md +94 -0
- package/skills/system-design-interview/examples/before.md +27 -0
- package/skills/system-design-interview/references/api_reference.md +582 -0
- package/skills/system-design-interview/references/review-checklist.md +201 -0
- package/skills/system-design-interview/scripts/example.py +1 -0
- package/skills/system-design-interview/scripts/new_design.py +421 -0
- package/skills/using-asyncio-python/SKILL.md +290 -0
- package/skills/using-asyncio-python/assets/example_asset.txt +1 -0
- package/skills/using-asyncio-python/evals/evals.json +43 -0
- package/skills/using-asyncio-python/evals/results.json +13 -0
- package/skills/using-asyncio-python/examples/after.md +68 -0
- package/skills/using-asyncio-python/examples/before.md +39 -0
- package/skills/using-asyncio-python/references/api_reference.md +267 -0
- package/skills/using-asyncio-python/references/review-checklist.md +149 -0
- package/skills/using-asyncio-python/scripts/check_blocking.py +270 -0
- package/skills/using-asyncio-python/scripts/example.py +1 -0
- package/skills/web-scraping-python/SKILL.md +280 -0
- package/skills/web-scraping-python/assets/example_asset.txt +1 -0
- package/skills/web-scraping-python/evals/evals.json +46 -0
- package/skills/web-scraping-python/evals/results.json +13 -0
- package/skills/web-scraping-python/examples/after.md +109 -0
- package/skills/web-scraping-python/examples/before.md +40 -0
- package/skills/web-scraping-python/references/api_reference.md +393 -0
- package/skills/web-scraping-python/references/review-checklist.md +163 -0
- package/skills/web-scraping-python/scripts/example.py +1 -0
- package/skills/web-scraping-python/scripts/new_scraper.py +231 -0
- package/skills/writing-plans/audit.json +34 -0
- package/tests/agent-detector.test.js +83 -0
- package/tests/corrections.test.js +245 -0
- package/tests/doctor/hook-installer.test.js +72 -0
- package/tests/doctor/usage-tracker.test.js +140 -0
- package/tests/engine/benchmark-eval.test.js +31 -0
- package/tests/engine/bm25-index.test.js +85 -0
- package/tests/engine/capture-command.test.js +35 -0
- package/tests/engine/capture.test.js +17 -0
- package/tests/engine/graph-augmented-search.test.js +107 -0
- package/tests/engine/graph-injector.test.js +44 -0
- package/tests/engine/graph.test.js +216 -0
- package/tests/engine/hybrid-searcher.test.js +74 -0
- package/tests/engine/indexer-bm25.test.js +37 -0
- package/tests/engine/mcp-tools.test.js +73 -0
- package/tests/engine/project-initializer-mcp.test.js +99 -0
- package/tests/engine/query-expander.test.js +36 -0
- package/tests/engine/reranker.test.js +51 -0
- package/tests/engine/rrf.test.js +49 -0
- package/tests/engine/srag-prefix.test.js +47 -0
- package/tests/instinct-block.test.js +23 -0
- package/tests/mcp-config-writer.test.js +60 -0
- package/tests/project-initializer-new-agents.test.js +48 -0
- package/tests/rules/rules-manager.test.js +230 -0
- package/tests/well-known-builder.test.js +40 -0
- package/tests/wizard/integration-detector.test.js +31 -0
- package/tests/wizard/project-detector.test.js +51 -0
- package/tests/wizard/prompt-session.test.js +61 -0
- package/tests/wizard/prompt.test.js +16 -0
- package/tests/wizard/registry-embeddings.test.js +35 -0
- package/tests/wizard/skill-recommender.test.js +34 -0
- package/tests/wizard/slot-count.test.js +25 -0
- package/vercel.json +21 -0
|
@@ -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
|
|
@@ -0,0 +1,371 @@
|
|
|
1
|
+
# Effective TypeScript — Practices Catalog
|
|
2
|
+
|
|
3
|
+
Deep before/after examples for the 20 most impactful items.
|
|
4
|
+
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
## Item 2: Know Which TypeScript Options You're Using
|
|
8
|
+
|
|
9
|
+
Always use `strict: true`. It enables `noImplicitAny` and `strictNullChecks` which catch the most common TypeScript bugs.
|
|
10
|
+
|
|
11
|
+
**Before:**
|
|
12
|
+
```json
|
|
13
|
+
{ "compilerOptions": {} }
|
|
14
|
+
```
|
|
15
|
+
**After:**
|
|
16
|
+
```json
|
|
17
|
+
{ "compilerOptions": { "strict": true, "target": "ES2020", "module": "commonjs" } }
|
|
18
|
+
```
|
|
19
|
+
|
|
20
|
+
---
|
|
21
|
+
|
|
22
|
+
## Item 9: Prefer Type Declarations to Type Assertions
|
|
23
|
+
|
|
24
|
+
**Before:**
|
|
25
|
+
```typescript
|
|
26
|
+
const user = {} as User; // bypasses type checking — user is actually empty
|
|
27
|
+
const input = document.getElementById('name') as HTMLInputElement;
|
|
28
|
+
```
|
|
29
|
+
**After:**
|
|
30
|
+
```typescript
|
|
31
|
+
const user: User = { id: '1', name: 'Alice', email: 'a@example.com' }; // checked
|
|
32
|
+
const input = document.getElementById('name');
|
|
33
|
+
if (input instanceof HTMLInputElement) {
|
|
34
|
+
console.log(input.value); // narrowed, not asserted
|
|
35
|
+
}
|
|
36
|
+
```
|
|
37
|
+
|
|
38
|
+
---
|
|
39
|
+
|
|
40
|
+
## Item 10: Avoid Object Wrapper Types
|
|
41
|
+
|
|
42
|
+
**Before:**
|
|
43
|
+
```typescript
|
|
44
|
+
function greet(name: String) { // String, not string
|
|
45
|
+
return 'Hello ' + name;
|
|
46
|
+
}
|
|
47
|
+
const s = new String('world');
|
|
48
|
+
greet(s); // works but s !== 'world' as a primitive
|
|
49
|
+
```
|
|
50
|
+
**After:**
|
|
51
|
+
```typescript
|
|
52
|
+
function greet(name: string) { // primitive type
|
|
53
|
+
return 'Hello ' + name;
|
|
54
|
+
}
|
|
55
|
+
```
|
|
56
|
+
|
|
57
|
+
---
|
|
58
|
+
|
|
59
|
+
## Item 13: Know the Differences Between `type` and `interface`
|
|
60
|
+
|
|
61
|
+
- Use `interface` for object shapes that consumers may need to extend (open for augmentation)
|
|
62
|
+
- Use `type` for unions, intersections, tuples, and mapped types (cannot be augmented)
|
|
63
|
+
|
|
64
|
+
**Before:**
|
|
65
|
+
```typescript
|
|
66
|
+
type User = { id: string; name: string }; // fine, but can't be augmented by consumers
|
|
67
|
+
type StringOrNumber = string | number; // correct use of type
|
|
68
|
+
```
|
|
69
|
+
**After:**
|
|
70
|
+
```typescript
|
|
71
|
+
interface User { id: string; name: string; } // open for extension
|
|
72
|
+
type StringOrNumber = string | number; // unions must be type aliases
|
|
73
|
+
type ReadonlyUser = Readonly<User>; // mapped type must be type alias
|
|
74
|
+
```
|
|
75
|
+
|
|
76
|
+
---
|
|
77
|
+
|
|
78
|
+
## Item 14: Use Type Operations and Generics to Avoid Repeating Yourself
|
|
79
|
+
|
|
80
|
+
**Before:**
|
|
81
|
+
```typescript
|
|
82
|
+
interface SavedState { userId: string; name: string; lastSaved: Date; }
|
|
83
|
+
interface UnsavedState { userId: string; name: string; } // repeated fields
|
|
84
|
+
```
|
|
85
|
+
**After:**
|
|
86
|
+
```typescript
|
|
87
|
+
interface State { userId: string; name: string; }
|
|
88
|
+
interface SavedState extends State { lastSaved: Date; }
|
|
89
|
+
// Or with Pick/Omit:
|
|
90
|
+
type UnsavedState = Omit<SavedState, 'lastSaved'>;
|
|
91
|
+
```
|
|
92
|
+
|
|
93
|
+
---
|
|
94
|
+
|
|
95
|
+
## Item 17: Use `readonly` to Avoid Errors Associated with Mutation
|
|
96
|
+
|
|
97
|
+
**Before:**
|
|
98
|
+
```typescript
|
|
99
|
+
function sort(arr: number[]): number[] {
|
|
100
|
+
return arr.sort(); // mutates the original array!
|
|
101
|
+
}
|
|
102
|
+
```
|
|
103
|
+
**After:**
|
|
104
|
+
```typescript
|
|
105
|
+
function sort(arr: readonly number[]): number[] {
|
|
106
|
+
return [...arr].sort(); // forced to copy — cannot mutate readonly input
|
|
107
|
+
}
|
|
108
|
+
```
|
|
109
|
+
|
|
110
|
+
---
|
|
111
|
+
|
|
112
|
+
## Item 22: Understand Type Narrowing
|
|
113
|
+
|
|
114
|
+
**Before:**
|
|
115
|
+
```typescript
|
|
116
|
+
function processInput(val: string | null) {
|
|
117
|
+
console.log(val.toUpperCase()); // error: val is possibly null
|
|
118
|
+
}
|
|
119
|
+
```
|
|
120
|
+
**After:**
|
|
121
|
+
```typescript
|
|
122
|
+
function processInput(val: string | null) {
|
|
123
|
+
if (val === null) return;
|
|
124
|
+
console.log(val.toUpperCase()); // narrowed to string
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
// instanceof narrowing
|
|
128
|
+
function format(val: Date | string) {
|
|
129
|
+
if (val instanceof Date) return val.toISOString();
|
|
130
|
+
return val.toUpperCase();
|
|
131
|
+
}
|
|
132
|
+
```
|
|
133
|
+
|
|
134
|
+
---
|
|
135
|
+
|
|
136
|
+
## Item 25: Use async Functions Instead of Callbacks for Asynchronous Code
|
|
137
|
+
|
|
138
|
+
Callbacks produce `any`-typed errors and complex nested types. `async`/`await` lets TypeScript infer `Promise<T>` cleanly.
|
|
139
|
+
|
|
140
|
+
**Before:**
|
|
141
|
+
```typescript
|
|
142
|
+
function fetchData(url: string, cb: (err: any, data: any) => void) {
|
|
143
|
+
fetch(url)
|
|
144
|
+
.then(r => r.json())
|
|
145
|
+
.then(d => cb(null, d))
|
|
146
|
+
.catch(e => cb(e, null));
|
|
147
|
+
}
|
|
148
|
+
```
|
|
149
|
+
**After:**
|
|
150
|
+
```typescript
|
|
151
|
+
async function fetchData<T>(url: string): Promise<T> {
|
|
152
|
+
const response = await fetch(url);
|
|
153
|
+
if (!response.ok) throw new Error(`HTTP ${response.status}`);
|
|
154
|
+
return response.json() as T;
|
|
155
|
+
}
|
|
156
|
+
```
|
|
157
|
+
|
|
158
|
+
---
|
|
159
|
+
|
|
160
|
+
## Item 28: Prefer Types That Always Represent Valid States
|
|
161
|
+
|
|
162
|
+
**Before:**
|
|
163
|
+
```typescript
|
|
164
|
+
interface Page {
|
|
165
|
+
pageText: string;
|
|
166
|
+
isLoading: boolean;
|
|
167
|
+
error: string; // What does isLoading:true + error:'...' mean?
|
|
168
|
+
}
|
|
169
|
+
```
|
|
170
|
+
**After:**
|
|
171
|
+
```typescript
|
|
172
|
+
type Page =
|
|
173
|
+
| { state: 'loading' }
|
|
174
|
+
| { state: 'success'; pageText: string }
|
|
175
|
+
| { state: 'error'; error: string };
|
|
176
|
+
// Every combination is meaningful — no impossible states
|
|
177
|
+
```
|
|
178
|
+
|
|
179
|
+
---
|
|
180
|
+
|
|
181
|
+
## Item 31: Push Null Values to the Perimeter
|
|
182
|
+
|
|
183
|
+
**Before:**
|
|
184
|
+
```typescript
|
|
185
|
+
// null scattered throughout
|
|
186
|
+
function getUser(id: string | null): User | null {
|
|
187
|
+
if (!id) return null;
|
|
188
|
+
const user: User | null = db.find(id) ?? null;
|
|
189
|
+
return user;
|
|
190
|
+
}
|
|
191
|
+
```
|
|
192
|
+
**After:**
|
|
193
|
+
```typescript
|
|
194
|
+
// null handled once at the boundary
|
|
195
|
+
function getUser(id: string): User {
|
|
196
|
+
const user = db.find(id);
|
|
197
|
+
if (!user) throw new Error(`User ${id} not found`);
|
|
198
|
+
return user;
|
|
199
|
+
}
|
|
200
|
+
// Callers who might not have an id handle it at their boundary
|
|
201
|
+
```
|
|
202
|
+
|
|
203
|
+
---
|
|
204
|
+
|
|
205
|
+
## Item 32: Prefer Unions of Interfaces to Interfaces of Unions
|
|
206
|
+
|
|
207
|
+
**Before:**
|
|
208
|
+
```typescript
|
|
209
|
+
interface Layer {
|
|
210
|
+
type: 'fill' | 'line' | 'point';
|
|
211
|
+
fillColor?: string; // only for fill
|
|
212
|
+
lineWidth?: number; // only for line
|
|
213
|
+
pointRadius?: number; // only for point
|
|
214
|
+
// { type: 'fill', lineWidth: 5 } is representable but invalid
|
|
215
|
+
}
|
|
216
|
+
```
|
|
217
|
+
**After:**
|
|
218
|
+
```typescript
|
|
219
|
+
interface FillLayer { type: 'fill'; fillColor: string; }
|
|
220
|
+
interface LineLayer { type: 'line'; lineWidth: number; }
|
|
221
|
+
interface PointLayer { type: 'point'; pointRadius: number; }
|
|
222
|
+
type Layer = FillLayer | LineLayer | PointLayer;
|
|
223
|
+
// Each valid state has exactly the right fields
|
|
224
|
+
```
|
|
225
|
+
|
|
226
|
+
---
|
|
227
|
+
|
|
228
|
+
## Item 33: Prefer More Precise Alternatives to String Types
|
|
229
|
+
|
|
230
|
+
**Before:**
|
|
231
|
+
```typescript
|
|
232
|
+
function setAlignment(align: string) {} // accepts anything
|
|
233
|
+
interface Album { genre: string; } // 'rock', 'jazz', or 'anything'?
|
|
234
|
+
```
|
|
235
|
+
**After:**
|
|
236
|
+
```typescript
|
|
237
|
+
type Alignment = 'left' | 'center' | 'right';
|
|
238
|
+
function setAlignment(align: Alignment) {}
|
|
239
|
+
|
|
240
|
+
type Genre = 'rock' | 'jazz' | 'pop' | 'classical';
|
|
241
|
+
interface Album { genre: Genre; }
|
|
242
|
+
```
|
|
243
|
+
|
|
244
|
+
---
|
|
245
|
+
|
|
246
|
+
## Item 38: Use the Narrowest Possible Scope for `any` Types
|
|
247
|
+
|
|
248
|
+
**Before:**
|
|
249
|
+
```typescript
|
|
250
|
+
const config: any = JSON.parse(rawConfig); // entire variable is any
|
|
251
|
+
console.log(config.timeout); // no type checking from here on
|
|
252
|
+
```
|
|
253
|
+
**After:**
|
|
254
|
+
```typescript
|
|
255
|
+
const config = JSON.parse(rawConfig) as { timeout: number; retries: number };
|
|
256
|
+
// or better — parse with unknown and narrow:
|
|
257
|
+
const raw: unknown = JSON.parse(rawConfig);
|
|
258
|
+
const timeout = (raw as { timeout: number }).timeout; // any scoped to one property access
|
|
259
|
+
```
|
|
260
|
+
|
|
261
|
+
---
|
|
262
|
+
|
|
263
|
+
## Item 40: Hide Unsafe Type Assertions in Well-Typed Functions
|
|
264
|
+
|
|
265
|
+
**Before:**
|
|
266
|
+
```typescript
|
|
267
|
+
// Assertion visible at every call site
|
|
268
|
+
const user = fetchUser() as User;
|
|
269
|
+
```
|
|
270
|
+
**After:**
|
|
271
|
+
```typescript
|
|
272
|
+
// Assertion encapsulated once — all callers get proper types
|
|
273
|
+
async function fetchUser(id: string): Promise<User> {
|
|
274
|
+
const raw: unknown = await fetch(`/api/users/${id}`).then(r => r.json());
|
|
275
|
+
return raw as User; // single controlled assertion inside typed boundary
|
|
276
|
+
}
|
|
277
|
+
```
|
|
278
|
+
|
|
279
|
+
---
|
|
280
|
+
|
|
281
|
+
## Item 42: Use `unknown` Instead of `any` for Values with an Unknown Type
|
|
282
|
+
|
|
283
|
+
**Before:**
|
|
284
|
+
```typescript
|
|
285
|
+
function parseYaml(yaml: string): any { // callers never have to narrow
|
|
286
|
+
return parse(yaml);
|
|
287
|
+
}
|
|
288
|
+
const config = parseYaml(raw);
|
|
289
|
+
config.port.toFixed(); // no error — but crashes if port is missing
|
|
290
|
+
```
|
|
291
|
+
**After:**
|
|
292
|
+
```typescript
|
|
293
|
+
function parseYaml(yaml: string): unknown { // callers must narrow before use
|
|
294
|
+
return parse(yaml);
|
|
295
|
+
}
|
|
296
|
+
const config = parseYaml(raw);
|
|
297
|
+
if (typeof config === 'object' && config !== null && 'port' in config) {
|
|
298
|
+
const port = (config as { port: number }).port;
|
|
299
|
+
}
|
|
300
|
+
```
|
|
301
|
+
|
|
302
|
+
---
|
|
303
|
+
|
|
304
|
+
## Item 47: Export All Types That Appear in Public APIs
|
|
305
|
+
|
|
306
|
+
**Before:**
|
|
307
|
+
```typescript
|
|
308
|
+
// Unexported — users have to use ReturnType<typeof getUser> hacks
|
|
309
|
+
interface User { id: string; name: string; }
|
|
310
|
+
export function getUser(id: string): User { ... }
|
|
311
|
+
```
|
|
312
|
+
**After:**
|
|
313
|
+
```typescript
|
|
314
|
+
export interface User { id: string; name: string; } // exported explicitly
|
|
315
|
+
export function getUser(id: string): User { ... }
|
|
316
|
+
```
|
|
317
|
+
|
|
318
|
+
---
|
|
319
|
+
|
|
320
|
+
## Item 48: Use TSDoc for API Comments
|
|
321
|
+
|
|
322
|
+
**Before:**
|
|
323
|
+
```typescript
|
|
324
|
+
// Fetches user by id. Returns null if not found.
|
|
325
|
+
function getUser(id: string): User | null { ... }
|
|
326
|
+
```
|
|
327
|
+
**After:**
|
|
328
|
+
```typescript
|
|
329
|
+
/**
|
|
330
|
+
* Fetches a user by their unique identifier.
|
|
331
|
+
*
|
|
332
|
+
* @param id - The user's unique identifier
|
|
333
|
+
* @returns The user, or null if no user exists with that id
|
|
334
|
+
* @throws {NetworkError} If the network request fails
|
|
335
|
+
*/
|
|
336
|
+
function getUser(id: string): User | null { ... }
|
|
337
|
+
```
|
|
338
|
+
|
|
339
|
+
---
|
|
340
|
+
|
|
341
|
+
## Item 53: Prefer ECMAScript Features to TypeScript Features
|
|
342
|
+
|
|
343
|
+
Avoid TypeScript-specific features that don't map cleanly to JavaScript. Prefer standard ES features.
|
|
344
|
+
|
|
345
|
+
**Before:**
|
|
346
|
+
```typescript
|
|
347
|
+
// TypeScript enums — compiled to objects with side effects, can't be tree-shaken
|
|
348
|
+
enum Direction { North, South, East, West }
|
|
349
|
+
```
|
|
350
|
+
**After:**
|
|
351
|
+
```typescript
|
|
352
|
+
// Const object + type — tree-shakeable, maps cleanly to JS
|
|
353
|
+
const Direction = { North: 'North', South: 'South', East: 'East', West: 'West' } as const;
|
|
354
|
+
type Direction = typeof Direction[keyof typeof Direction];
|
|
355
|
+
```
|
|
356
|
+
|
|
357
|
+
---
|
|
358
|
+
|
|
359
|
+
## Item 62: Don't Consider Migration Complete Until You Enable `noImplicitAny`
|
|
360
|
+
|
|
361
|
+
The final step of any JS→TS migration. Without it, TypeScript silently accepts untyped code.
|
|
362
|
+
|
|
363
|
+
```json
|
|
364
|
+
// tsconfig.json — final migration milestone
|
|
365
|
+
{
|
|
366
|
+
"compilerOptions": {
|
|
367
|
+
"strict": true, // includes noImplicitAny and strictNullChecks
|
|
368
|
+
"noImplicitAny": true // explicit — every value must have a type
|
|
369
|
+
}
|
|
370
|
+
}
|
|
371
|
+
```
|
|
@@ -0,0 +1,169 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
"""
|
|
3
|
+
review.py — Pre-analysis script for Effective TypeScript reviews.
|
|
4
|
+
Usage: python review.py <file.ts|file.tsx>
|
|
5
|
+
|
|
6
|
+
Scans a TypeScript source file for anti-patterns from the book's 62 items:
|
|
7
|
+
any usage, type assertions, object wrapper types, non-null assertions,
|
|
8
|
+
missing strict mode, interface-of-unions, plain string types, and more.
|
|
9
|
+
"""
|
|
10
|
+
|
|
11
|
+
import re
|
|
12
|
+
import sys
|
|
13
|
+
from pathlib import Path
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
CHECKS = [
|
|
17
|
+
(
|
|
18
|
+
r":\s*any\b",
|
|
19
|
+
"Item 5/38: any type annotation",
|
|
20
|
+
"replace with a specific type, generic parameter, or unknown for truly unknown values",
|
|
21
|
+
),
|
|
22
|
+
(
|
|
23
|
+
r"\bas\s+any\b",
|
|
24
|
+
"Item 38/40: 'as any' assertion",
|
|
25
|
+
"scope 'as any' as narrowly as possible — hide inside a well-typed wrapper function; prefer 'as unknown as T' for safer double assertion",
|
|
26
|
+
),
|
|
27
|
+
(
|
|
28
|
+
r"\bString\b|\bNumber\b|\bBoolean\b|\bObject\b|\bSymbol\b",
|
|
29
|
+
"Item 10: Object wrapper type (String/Number/Boolean)",
|
|
30
|
+
"use primitive types: string, number, boolean — never the wrapper class types",
|
|
31
|
+
),
|
|
32
|
+
(
|
|
33
|
+
r"!\.",
|
|
34
|
+
"Item 28/31: Non-null assertion (!).",
|
|
35
|
+
"non-null assertions are usually a symptom of an imprecise type — fix the type instead; consider optional chaining (?.) or a type guard",
|
|
36
|
+
),
|
|
37
|
+
(
|
|
38
|
+
r"@ts-ignore|@ts-nocheck",
|
|
39
|
+
"Item 38: @ts-ignore suppresses type errors",
|
|
40
|
+
"fix the underlying type issue; if unavoidable use @ts-expect-error with a comment explaining why",
|
|
41
|
+
),
|
|
42
|
+
(
|
|
43
|
+
r"function\s+\w+[^{]*\{[^}]{0,20}\}",
|
|
44
|
+
None, # skip — too noisy
|
|
45
|
+
None,
|
|
46
|
+
),
|
|
47
|
+
(
|
|
48
|
+
r"interface\s+\w+\s*\{[^}]*\?[^}]*\?[^}]*\}",
|
|
49
|
+
"Item 32: Interface with multiple optional fields",
|
|
50
|
+
"multiple optional fields that have implicit relationships suggest an interface-of-unions — convert to a tagged discriminated union",
|
|
51
|
+
),
|
|
52
|
+
(
|
|
53
|
+
r"param(?:eter)?\s*:\s*string(?!\s*[|&])",
|
|
54
|
+
"Item 33: Plain string parameter",
|
|
55
|
+
"consider a string literal union if the parameter has a finite set of valid values (e.g. 'asc' | 'desc')",
|
|
56
|
+
),
|
|
57
|
+
(
|
|
58
|
+
r"\.json\(\)\s*as\s+\w",
|
|
59
|
+
"Item 9/40: Direct type assertion on .json()",
|
|
60
|
+
"assign to unknown first, then narrow: 'const raw: unknown = await res.json()' — assertion inside a well-typed wrapper is acceptable (Item 40)",
|
|
61
|
+
),
|
|
62
|
+
(
|
|
63
|
+
r"Promise<any>",
|
|
64
|
+
"Item 38: Promise<any> return type",
|
|
65
|
+
"replace with Promise<unknown> or a concrete type — Promise<any> disables type checking on the resolved value",
|
|
66
|
+
),
|
|
67
|
+
]
|
|
68
|
+
|
|
69
|
+
|
|
70
|
+
def scan(source: str) -> list[dict]:
|
|
71
|
+
findings = []
|
|
72
|
+
lines = source.splitlines()
|
|
73
|
+
for lineno, line in enumerate(lines, start=1):
|
|
74
|
+
stripped = line.strip()
|
|
75
|
+
if stripped.startswith("//") or stripped.startswith("*"):
|
|
76
|
+
continue
|
|
77
|
+
for pattern, label, advice in CHECKS:
|
|
78
|
+
if label is None:
|
|
79
|
+
continue
|
|
80
|
+
if re.search(pattern, line):
|
|
81
|
+
findings.append({
|
|
82
|
+
"line": lineno,
|
|
83
|
+
"text": line.rstrip(),
|
|
84
|
+
"label": label,
|
|
85
|
+
"advice": advice,
|
|
86
|
+
})
|
|
87
|
+
return findings
|
|
88
|
+
|
|
89
|
+
|
|
90
|
+
def check_strict(source: str) -> bool:
|
|
91
|
+
"""Returns True if this looks like a tsconfig with strict mode enabled."""
|
|
92
|
+
return bool(re.search(r'"strict"\s*:\s*true', source))
|
|
93
|
+
|
|
94
|
+
|
|
95
|
+
def sep(char="-", width=70) -> str:
|
|
96
|
+
return char * width
|
|
97
|
+
|
|
98
|
+
|
|
99
|
+
def main() -> None:
|
|
100
|
+
if len(sys.argv) < 2:
|
|
101
|
+
print("Usage: python review.py <file.ts|file.tsx>")
|
|
102
|
+
sys.exit(1)
|
|
103
|
+
|
|
104
|
+
path = Path(sys.argv[1])
|
|
105
|
+
if not path.exists():
|
|
106
|
+
print(f"Error: file not found: {path}")
|
|
107
|
+
sys.exit(1)
|
|
108
|
+
|
|
109
|
+
if path.suffix.lower() not in (".ts", ".tsx", ".json"):
|
|
110
|
+
print(f"Warning: expected .ts/.tsx, got '{path.suffix}' — continuing anyway")
|
|
111
|
+
|
|
112
|
+
source = path.read_text(encoding="utf-8", errors="replace")
|
|
113
|
+
|
|
114
|
+
# Special case: tsconfig.json
|
|
115
|
+
if path.name == "tsconfig.json":
|
|
116
|
+
print(sep("="))
|
|
117
|
+
print("EFFECTIVE TYPESCRIPT — TSCONFIG CHECK")
|
|
118
|
+
print(sep("="))
|
|
119
|
+
if check_strict(source):
|
|
120
|
+
print(" [OK] strict: true is enabled (Item 2)")
|
|
121
|
+
else:
|
|
122
|
+
print(" [!] strict: true is NOT enabled — Item 2: always enable strict mode")
|
|
123
|
+
print(" Add: \"strict\": true to compilerOptions")
|
|
124
|
+
print()
|
|
125
|
+
print(sep("="))
|
|
126
|
+
return
|
|
127
|
+
|
|
128
|
+
findings = scan(source)
|
|
129
|
+
groups: dict[str, list] = {}
|
|
130
|
+
for f in findings:
|
|
131
|
+
groups.setdefault(f["label"], []).append(f)
|
|
132
|
+
|
|
133
|
+
print(sep("="))
|
|
134
|
+
print("EFFECTIVE TYPESCRIPT — PRE-REVIEW REPORT")
|
|
135
|
+
print(sep("="))
|
|
136
|
+
print(f"File : {path}")
|
|
137
|
+
print(f"Lines : {len(source.splitlines())}")
|
|
138
|
+
print(f"Issues : {len(findings)} potential violations across {len(groups)} categories")
|
|
139
|
+
print()
|
|
140
|
+
|
|
141
|
+
if not findings:
|
|
142
|
+
print(" [OK] No common Effective TypeScript anti-patterns detected.")
|
|
143
|
+
print()
|
|
144
|
+
else:
|
|
145
|
+
for label, items in groups.items():
|
|
146
|
+
print(sep())
|
|
147
|
+
print(f" {label} ({len(items)} occurrence{'s' if len(items) != 1 else ''})")
|
|
148
|
+
print(sep())
|
|
149
|
+
print(f" Advice: {items[0]['advice']}")
|
|
150
|
+
print()
|
|
151
|
+
for item in items[:5]:
|
|
152
|
+
print(f" line {item['line']:>4}: {item['text'][:100]}")
|
|
153
|
+
if len(items) > 5:
|
|
154
|
+
print(f" ... and {len(items) - 5} more")
|
|
155
|
+
print()
|
|
156
|
+
|
|
157
|
+
severity = (
|
|
158
|
+
"HIGH" if len(findings) >= 5
|
|
159
|
+
else "MEDIUM" if len(findings) >= 2
|
|
160
|
+
else "LOW" if findings
|
|
161
|
+
else "NONE"
|
|
162
|
+
)
|
|
163
|
+
print(sep("="))
|
|
164
|
+
print(f"SEVERITY: {severity} | Key items: Item 2 (strict), Item 5/38 (any/unknown), Item 9 (assertions), Item 28/32 (tagged unions), Item 33 (literal types)")
|
|
165
|
+
print(sep("="))
|
|
166
|
+
|
|
167
|
+
|
|
168
|
+
if __name__ == "__main__":
|
|
169
|
+
main()
|