# TypeScript / JavaScript

Modern patterns, tooling, and best practices for the JavaScript/TypeScript ecosystem.

---

## TypeScript Essentials

### Strict Mode (Always Enable)
```json
{
  "compilerOptions": {
    "strict": true,
    "noUncheckedIndexedAccess": true,
    "exactOptionalPropertyTypes": true,
    "noImplicitReturns": true,
    "noFallthroughCasesInSwitch": true
  }
}
```

### Type Design Principles
- **Model domain, not data shapes** — `type UserId = string & { readonly brand: unique symbol }`
- **Discriminated unions** over optional fields for state machines
- **Zod/Arktype** for runtime validation + static types (single source of truth)
- **Avoid `any`** — Use `unknown` + type guards; enable `noImplicitAny`

### Functional Patterns
```typescript
// Result type for error handling
type Result<T, E = Error> = 
  | { ok: true; value: T }
  | { ok: false; error: E };

// Option type for nullable
type Option<T> = T | null;

// Pipeline / composition
const pipe = <T>(...fns: Array<(x: T) => T>) => (x: T) => fns.reduce((v, f) => f(v), x);
```

---

## Modern JavaScript (ES2022+)

### Key Features to Use
- **Top-level await** — Modules as async entry points
- **Private fields** — `#field` for true encapsulation
- **Logical assignment** — `??=`, `||=`, `&&=`
- **Array grouping** — `Object.groupBy()`, `Map.groupBy()`
- **Temporal API** — `Temporal.PlainDate`, `ZonedDateTime` (stage 3)

### Avoid
- `var` — Use `const` / `let`
- `for...in` on arrays — Use `for...of` or `.forEach()`
- Callback hell — Use `async/await` or Promise combinators
- Mutating arguments — Treat params as immutable

---

## Tooling Stack (2024+)

| Category | Recommended | Alternatives |
|---|---|---|
| **Runtime** | Node.js 20+ (LTS) | Bun, Deno |
| **Package Manager** | pnpm (fast, disk-efficient) | npm v10+, yarn 4+ |
| **Build** | Vite (dev), esbuild/tsup (lib) | Webpack, Rollup, Turbopack |
| **Type Checking** | `tsc --noEmit` + `tsx` for dev | `tsc` watch mode |
| **Linting** | ESLint (flat config) + `@typescript-eslint` | Biome (fast, integrated) |
| **Formatting** | Prettier (or Biome) | dprint |
| **Testing** | Vitest (unit), Playwright (E2E) | Jest, Cypress |
| **Type Generation** | Zod + `zod-to-ts`, or Arktype | io-ts, valibot |

### Recommended ESLint Flat Config
```js
// eslint.config.js
export default [
  { ignores: ['dist/', 'node_modules/'] },
  ...eslint.configs.recommended,
  ...eslintTypescript.configs.recommended,
  ...eslintPrettier.configs.recommended,
  { rules: { '@typescript-eslint/no-explicit-any': 'error' } }
];
```

---

## Project Structure

```
src/
├── domain/           # Pure business logic (no framework deps)
│   ├── entities/
│   ├── value-objects/
│   ├── services/
│   └── repositories/ # Interfaces only
├── application/      # Use cases, orchestration
│   ├── commands/
│   ├── queries/
│   └── dtos/
├── infrastructure/   # Framework implementations
│   ├── persistence/  # Repository impls (Prisma, Drizzle, TypeORM)
│   ├── http/         # Controllers, routes (Fastify, Hono, Express)
│   ├── messaging/    # Event publishers/consumers
│   └── config/       # Env validation (Zod)
├── presentation/     # API layer (tRPC, GraphQL, REST)
└── main.ts           # Composition root (DI wiring)
```

---

## Database Access

### ORM vs Query Builder
| Approach | When |
|---|---|
| **Prisma** | Full ORM; type-safe; migrations; good for standard CRUD |
| **Drizzle** | Lightweight; SQL-like; better for complex queries |
| **Kysely** | Type-safe query builder; no migrations; full control |
| **Raw SQL + pg** | Maximum control; complex analytics; performance-critical |

### Repository Pattern (Domain-Driven)
```typescript
// domain/repositories/user-repository.ts
export interface UserRepository {
  findById(id: UserId): Promise<Option<User>>;
  save(user: User): Promise<void>;
}

// infrastructure/persistence/prisma-user-repository.ts
export class PrismaUserRepository implements UserRepository {
  constructor(private prisma: PrismaClient) {}
  async findById(id: UserId) { /* ... */ }
  async save(user: User) { /* ... */ }
}
```

---

## API Layer

### tRPC (End-to-End Typesafe)
```typescript
// Server
export const appRouter = router({
  user: router({
    byId: protectedProcedure
      .input(z.object({ id: UserIdSchema }))
      .query(({ ctx, input }) => ctx.userRepo.findById(input.id)),
  }),
});

// Client — fully typed
const user = await trpc.user.byId({ id: '123' });
```

### REST with OpenAPI
- **Hono** + `@hono/zod-openapi` for runtime validation + OpenAPI generation
- **Fastify** + `fastify-type-provider-zod` for type-safe routes

---

## State Management (Frontend)

| Scale | Solution |
|---|---|
| **Local component** | `useState`, `useReducer` |
| **Cross-component** | Zustand, Jotai, Valtio (atomic) |
| **Server state** | TanStack Query (React Query) — caching, deduping, invalidation |
| **Forms** | React Hook Forms + Zod resolver |
| **URL state** | `nuqs`, `search-params` |

---

## Concurrency & Async Patterns

### Structured Concurrency
```typescript
// Run with timeout + cancellation
const result = await Promise.race([
  doWork(),
  new Promise((_, reject) => setTimeout(() => reject(new TimeoutError()), 5000))
]);

// Parallel with concurrency limit (p-map, bottleneck)
import pMap from 'p-map';
await pMap(items, processItem, { concurrency: 5 });
```

### Worker Threads (CPU-bound)
```typescript
// main.ts
const worker = new Worker('./worker.ts', { workerData: input });
worker.on('message', handleResult);

// worker.ts
parentPort!.on('message', (data) => {
  const result = heavyComputation(data);
  parentPort!.postMessage(result);
});
```

---

## Testing Patterns

### Unit (Vitest)
```typescript
import { describe, it, expect, vi } from 'vitest';

describe('PricingService', () => {
  it('applies discount for premium users', () => {
    const repo = { findUser: vi.fn().mockResolvedValue({ tier: 'premium' }) };
    const service = new PricingService(repo);
    const price = await service.calculate(userId, basePrice);
    expect(price).toBe(basePrice * 0.9);
  });
});
```

### Contract Tests (Pact)
```typescript
// Consumer test
await provider.addInteraction({
  state: 'user exists',
  uponReceiving: 'request for user',
  withRequest: { method: 'GET', path: '/users/123' },
  willRespondWith: { status: 200, body: like({ id: '123', name: 'John' }) },
});
```

---

## Performance

- **Bundle analysis** — `vite-bundle-analyzer`, `webpack-bundle-analyzer`
- **Tree shaking** — ESM only; sideEffects: false in package.json
- **Code splitting** — Dynamic `import()`; route-based chunks
- **Streaming SSR** — React 18 `renderToPipeableStream`, Astro, Next.js
- **Edge runtime** — Cloudflare Workers, Vercel Edge, Deno Deploy

---

## Security

- **Dependency scanning** — `pnpm audit`, `npm audit`, Snyk, Socket
- **CSP headers** — Nonce-based for inline scripts
- **Sanitization** — DOMPurify for user HTML; Zod for all inputs
- **Secrets** — Never in code; use env vars + secret manager

---

## Related Notes

- [[Languages/Language-Agnostic-Principles]] — Universal patterns
- [[Languages/Python]] — Python patterns
- [[Languages/Go]] — Go patterns
- [[Patterns/Design-Patterns]] — Patterns in TS/JS
- [[Architecture/Architectural-Styles]] — Hexagonal in TS
- [[Testing/Test-Pyramid]] — Testing strategy
- [[DevOps/CI-CD-Best-Practices]] — Pipeline for JS/TS

---

*Sources: TypeScript Handbook, Effective TypeScript (Dan Vanderkam), Matt Pocock (Total TypeScript), React Query Docs, TanStack, Vite, Biome, Zod, tRPC, Hono, Fastify, Node.js Best Practices*