TypeScript Conventions
Idioms
- •Enable strict mode in
tsconfig.json— no exceptions - •Prefer
constoverlet; never usevar - •Use discriminated unions for state modeling over class hierarchies
- •Prefer
unknownoverany— narrow types explicitly - •Use
readonlyon properties and arrays that should not be mutated - •Prefer
interfacefor object shapes,typefor unions and intersections - •Use template literal types for string patterns where useful
- •Prefer
satisfiesoperator to validate types without widening
Project Structure
- •Source code in
src/with a clear module structure - •Use path aliases (
@/or~/) configured in tsconfig for deep imports - •Barrel exports (
index.ts) only at package boundaries — avoid within a package - •Colocate related files: components with their tests, types, and styles
- •Configuration files at project root:
tsconfig.json,package.json
Testing
- •Use vitest as the test framework
- •Colocate test files next to source:
thing.tsandthing.test.ts - •Use
describe/itblocks with clear, behavior-focused descriptions - •Prefer
expect(...).toBe()for primitives,toEqual()for objects - •Mock external dependencies at module boundaries, not internal functions
- •Use
vi.fn()for spies andvi.mock()for module mocks
Error Handling
- •Define typed error classes extending
Errorwith acodeproperty - •Use a Result pattern (
{ ok: true, data } | { ok: false, error }) for expected failures - •Reserve
try/catchfor unexpected errors at system boundaries - •Never swallow errors silently — log or rethrow
- •Validate external input at boundaries with zod; trust internal types
Dependencies
- •Validation: zod for runtime schema validation
- •Dates: date-fns (tree-shakeable, functional)
- •HTTP client: native fetch, or ofetch for convenience
- •Linting/formatting: eslint + prettier, or biome
- •Build: tsup for libraries, vite for applications
Anti-patterns
- •Using
anyto bypass type checking — useunknownand narrow - •Overusing
enum— preferas constobjects or union types - •Barrel files (
index.ts) deep within packages — causes bundling and circular import issues - •Mutation of shared state — prefer immutable patterns and pure functions
- •Non-null assertion (
!) as a habit — narrow types properly instead - •Ignoring
Promisereturn values — alwaysawaitor explicitlyvoid