DEV Community

Cover image for Fast & Clean TypeScript Workflow with Environment Variables, Bun, and ts-node-dev
Bello Osagie
Bello Osagie

Posted on

Fast & Clean TypeScript Workflow with Environment Variables, Bun, and ts-node-dev

Here's how to bootstrap and evolve your Express‑style TypeScript app step by step:


1. Project Initialization

Initialize your project and TypeScript entry point:

npm init -y
mkdir src
touch src/app.ts .env.local .gitignore
Enter fullscreen mode Exit fullscreen mode

Your package.json now has defaults, and you have:

  • src/app.ts – your app entry
  • .env.local – development env file
  • .gitignore – to protect sensitive files

2. Environment Variables in .env.local

# .env.local
PORT=5000
Enter fullscreen mode Exit fullscreen mode

This file sets PORT=5000 for development, allowing differentiation from production.


3. TypeScript & @types/node Setup

Install type definitions for Node.js to fix lint issues like process.env.PORT:

npm install -D @types/node
Enter fullscreen mode Exit fullscreen mode

@types/node provides TypeScript types for built-in Node APIs, improving IDE feedback.


4. Reading Env & Demo Outputs

// src/app.ts
const PORT = process.env.PORT;
console.log(`PORT is ${PORT}`); // undefined w/o loading env file

enum MyEnum { A, B }
console.log(MyEnum.A);

// Type stripping demo with Node
const calcSum = (a: number, b: number) => a + b;
const calc = calcSum(4, 10);
console.log(`The sum is ${calc}`);
Enter fullscreen mode Exit fullscreen mode
  • With node src/app.ts, .env.local is not loaded, so PORT is undefined.
  • Node runs TS by stripping type annotations, even if types mismatch—it doesn't type-check at runtime.

5. Scripts & Loading .env.local

In package.json:

"scripts": {
  "dev": "node --env-file=.env.local src/app.ts"
}
Enter fullscreen mode Exit fullscreen mode
  • npm run dev now loads .env.local, so PORT logs 5000.
  • For production, switch to .env with "start": "node --env-file=.env src/app.js".

6. Safe .gitignore

.env
.env.local
node_modules
dist
Enter fullscreen mode Exit fullscreen mode

This prevents secrets and build artifacts from leaking to GitHub, and node_modules is rebuilt via npm install, avoiding bloat.


7. Enable Watch Mode

Improve dev workflow:

"dev": "node --env-file=.env.local --watch src/app.ts"
Enter fullscreen mode Exit fullscreen mode
  • This watches for file changes without extra tools (no nodemon or ts-node).

8. Node’s Inline Type Stripping: Behavior

Node v23.6+ strips types automatically (no --experimental-strip-types needed). E.g.,:

const calcSum = (a: number, b: boolean) => a + b;
console.log(calcSum(3, true)); // logs 4 — no type-checking
Enter fullscreen mode Exit fullscreen mode

TypeScript errors won’t be caught at runtime—types serve only as editor aids.


9. Faster Dev with ts-node-dev

Install:

npm install -D ts-node-dev
Enter fullscreen mode Exit fullscreen mode

Use in scripts:

"scripts": {
  "dev": "tsnd --respawn src/app.ts"
}
Enter fullscreen mode Exit fullscreen mode
  • ts-node-dev keeps TypeScript compiler in memory and restarts faster than node-dev or nodemon.
  • Remove --transpile-only to enable full type-checking before runtime.
  • --respawn ensures clean restarts on file change.

10. Build Output is In-Memory

Even with tsconfig.json configured (rootDir: "src", outDir: "dist"), ts-node-dev runs in-memory—so no dist folder appears unless you compile manually.


11. Bun—Fast, but Selective Support

You experimented with Bun:

"scripts": {
  "dev": "bun --watch src/app.ts",
  "check": "tsc --noEmit",
  "build": "bun build src/app.ts --outdir dist",
  "start": "bun run dist/app.js"
}
Enter fullscreen mode Exit fullscreen mode
  • Bun is extremely fast and first-class TS, but OS support is not as universal as Node.
  • Like Node, Bun strips types only and relies on editor + tsc for type-checking.

Summary Workflow

Phase Command Notes
Dev npm run dev Loads .env.local, watches files, no type-check
Build npm run check && tsc && bun build Type-check + compile to dist/
Production bun run dist/app.js or node dist/app.js Run compiled code for consistency across OS

TL;DR

  • Use .env.local for dev, .env for prod, and ignore both in Git.
  • Node/Bun auto-strip TS types—they don’t type-check at runtime.
  • For speed: use ts-node-dev or Bun’s watch.
  • For safety: run tsc --noEmit during build or CI.
  • Choose Node as your runtime for maximum OS compatibility; Bun is optional for speed gains.

Top comments (0)