Skip to main content

TypeScript

Every package in Refract is TypeScript-first. Each one has its own tsconfig.json tuned to what it needs to produce — a CommonJS library, an ES module bundle, or a browser SPA. Here’s a tour of what’s set up and why.

Backend (apps/backend/tsconfig.json)

The backend compiles to CommonJS for Node.js. It’s intentionally minimal — the goal is to get a clean dist/ for production, and the test suite is excluded from compilation entirely:
A few things worth noting:
  • esModuleInterop: true lets you write import Stripe from 'stripe' instead of import * as Stripe from 'stripe' for CommonJS modules that don’t have a default export.
  • resolveJsonModule: true allows importing .json files directly — handy for things like jest.config.json.
  • Test files are excluded because ts-jest handles their compilation separately at test time.

Frontend (apps/portal/tsconfig.app.json)

The frontend uses a split config: tsconfig.app.json for the application source and tsconfig.node.json for Vite’s config file. The top-level tsconfig.json just references both. tsconfig.app.json is where the interesting settings live:
Key things here:
  • moduleResolution: "bundler" is the modern mode for Vite/esbuild — it understands bare specifiers and TypeScript extensions without the old Node resolution quirks.
  • noEmit: true means TypeScript is purely a type-checker here. Vite handles the actual compilation.
  • noUnusedLocals and noUnusedParameters are turned on — TypeScript will error if you declare a variable or parameter you never use. This catches a lot of accidental dead code.
  • noUncheckedSideEffectImports prevents imports that exist only for side effects without an explicit acknowledgement — helps keep the import graph intentional.

Shared (apps/shared/tsconfig.json)

The shared package outputs both declarations and source maps alongside CommonJS JS, because it’s consumed by both the backend and the tool packages:
declaration: true and declarationMap: true produce the .d.ts files that let the backend and tool packages get full type information when importing from apps/shared. If you add a new type or schema to shared, run make build on the shared package before the backend will pick it up.

Tool packages (apps/tools/<tool>/<impl>/tsconfig.json)

All tool packages (BullMQ, SQS, Pino, Console, StatsD, etc.) share the same config shape as apps/shared — CommonJS output with declarations and source maps:
This is intentional — all tool packages are plug-in libraries and need to ship types so the backend can use them with full type safety.

Building

The build-all command is what CI uses. Note that backend tests depend on the compiled dist/ of all tool packages — so if you’re running backend tests locally and something isn’t found, a make build-all will usually fix it.
💡 Tip: if you’re adding a new tool package, make sure to run make deps-install after adding it to pnpm-workspace.yaml. Otherwise the workspace won’t know about it and make build-all won’t include it.

What’s next?

  • Linter — ESLint and Prettier configuration.
  • Testing — how ts-jest handles TypeScript compilation in tests.
  • Monorepo — how packages reference each other across the workspace.