TypeScript in Node
TypeScript on Node has changed a lot - ts-node, tsx, and bun all work, but esbuild + tsc strict types is the steady-state.
Node + TypeScript setup
EXAMPLE
// 1. Install
// npm i -D typescript @types/node tsx
// 2. tsconfig.json - strict baseline
{
'compilerOptions': {
'target': 'ES2022',
'module': 'NodeNext',
'moduleResolution': 'NodeNext',
'lib': ['ES2022'],
'strict': true,
'noUncheckedIndexedAccess': true,
'exactOptionalPropertyTypes': true,
'noImplicitOverride': true,
'noFallthroughCasesInSwitch': true,
'forceConsistentCasingInFileNames': true,
'isolatedModules': true,
'declaration': true,
'sourceMap': true,
'outDir': 'dist',
'rootDir': 'src'
},
'include': ['src/**/*'],
'exclude': ['node_modules', 'dist']
}
// 3. package.json
{
'type': 'module',
'main': 'dist/index.js',
'scripts': {
'dev': 'tsx watch src/index.ts',
'build': 'tsc',
'start': 'node dist/index.js',
'typecheck': 'tsc --noEmit'
}
}
// 4. ESM-friendly imports - always include the .js extension
// Yes, in TypeScript source, you write the .js extension for relative imports.
import { db } from './db.js';
import { logger } from './logger.js';
// 5. Environment variables - validate at boot
import { z } from 'zod';
const Env = z.object({
PORT: z.coerce.number().default(3000),
DATABASE_URL: z.string().url(),
NODE_ENV: z.enum(['development', 'production', 'test']).default('development'),
});
export const env = Env.parse(process.env);
// 6. Type-safe routes - express with zod
import express, { Request, Response } from 'express';
import { z } from 'zod';
const router = express.Router();
const CreateUser = z.object({ email: z.string().email(), name: z.string().min(1) });
type CreateUser = z.infer<typeof CreateUser>;
router.post('/users', async (req: Request, res: Response) => {
const parsed = CreateUser.safeParse(req.body);
if (!parsed.success) return res.status(400).json(parsed.error.flatten());
const user: CreateUser = parsed.data;
// ... create user ...
res.status(201).json(user);
});
// 7. Production builds with tsc; reach for esbuild or tsup only if cold-start matters
Why it matters
Strict types catch bugs you would not see in tests. Validate env vars with Zod at boot - failure is loud and immediate. tsx for dev, tsc for build; reach for bundlers only when cold-start latency is a problem.
Tip: Tweak the snippet with Try it Yourself », then sit the quiz at the bottom of the page.
Example
Example
// tsconfig.json compilerOptions: // "target": "ES2022", "module": "NodeNext", "strict": true // Run with tsx: npx tsx src/index.tsTry it Yourself »
Discussion
Loading…