Starphix
Software Engineering / Sources
The Lab / Sources / Software Engineering / Languages/TypeScript-JavaScript.md

TypeScript / JavaScript

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


TypeScript Essentials

Strict Mode (Always Enable)

{
  "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

// 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
// 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)

// 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)

// 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

// 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)

// 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)

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)

// 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

  • Language Agnostic Principles — Universal patterns
  • Python — Python patterns
  • Go — Go patterns
  • Design Patterns — Patterns in TS/JS
  • Architectural Styles — Hexagonal in TS
  • Test Pyramid — Testing strategy
  • 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

From the Software Engineering research vault · CC BY 4.0 · all notes · raw markdown