TypeScript Coding Guidelines
Requirements
- Node.js ESM, TypeScript ≥ 5.8, Vitest ≥ 3, Zod ≥ 4.
Essentials
- Strict mode - Enable strict flags, avoid
@ts-ignorewithout comment+issue - Type safety - Explicit public types,
unknownoverany, derive from generated types - Imports - Use
import typefor type-only imports - Async/await - Handle errors explicitly, only use
asyncwithawait, see references/async-without-await.md, references/unnecessary-async-keywords.md - Immutability - Use
const/readonlywhere possible - Validation - Zod for I/O boundaries, infer types from schemas
- Linting - Never suppress with eslint-disable, fix root causes, see references/avoid-eslint-disable.md
- Template literals - Convert numbers with
String(value), see references/template-literals-require-string-conversion.md - Numeric literals - Use underscores in large numbers (
30_000), see references/numeric-separator-enforcement.md - Method references - Keep object references to maintain
thisbinding, see references/unbound-method-references.md - Environment - Use dot notation for
process.envaccess, see references/env-access-bracket-notation.md - Paradigm - Functional style → fp-guide; class/OO design → oop-guide
Gotchas
- Structural typing means
{ a: string }accepts{ a: string, b: number }silently: explicitsatisfiesis the way to catch unintended extras ascasts bypass the type system without check: prefer narrowing functions (type predicates) over caststypevsinterface: interfaces merge across declarations, types don't.declare global { interface Window { ... } }works,type Window = ...doesn'tunknownis the saferany, but it doesn't propagate; narrowing once doesn't carry across assignments- Module resolution depends on
tsconfigmoduleResolution(bundlervsnode16vsnodenext): wrong choice silently breaks deep imports
Progressive disclosure
- Read references/async-without-await.md - Load when seeing async functions that don't use await
- Read references/unnecessary-async-keywords.md - Load when simplifying synchronous controller functions
- Read references/avoid-eslint-disable.md - Load when tempted to suppress linting warnings
- Read references/template-literals-require-string-conversion.md - Load when inserting numbers in template literals
- Read references/numeric-separator-enforcement.md - Load when writing large numeric literals
- Read references/unbound-method-references.md - Load when passing methods as callbacks or references
- Read references/env-access-bracket-notation.md - Load when accessing process.env variables