TypeScript in Node Basics
8 examples to get you started with TypeScript in Node - 6 basic and 2 intermediate.
Search across all documentation pages
8 examples to get you started with TypeScript in Node - 6 basic and 2 intermediate.
npm install -D typescript tsx @types/node."type": "module" in package.json for ESM examples below.NodeNext aligns TypeScript with Node's module resolver.
{
"compilerOptions": {
"target": "ES2022",
"module": "NodeNext",
"moduleResolution": "NodeNext",
"strict": true,
"skipLibCheck": true,
"outDir": "dist",
"rootDir": "src"
},
"include": ["src"]
}strict enables strictNullChecks, noImplicitAny, and related flags together.skipLibCheck speeds builds - still typecheck your src.outDir/rootDir keep dist/ mirroring src/.Related: tsx vs tsc vs ts-node - dev vs prod pipelines
process.env values are string | undefined - narrow before use.
const port = Number(process.env.PORT ?? 3000);
if (Number.isNaN(port)) throw new Error('Invalid PORT');?? handles undefined; empty string needs explicit handling.@types/node ships with DefinitelyTyped-quality typings for Node 24 APIs.
import { readFile } from 'node:fs/promises';
import type { Stats } from 'node:fs';
const stat: Stats = await readFile('package.json').then(() => import('node:fs')).then(fs => fs.promises.stat('package.json'));import type for type-only imports with verbatimModuleSyntax.@types/node version should match your Node major (npm i -D @types/node@24).node: prefix in application code.TypeScript source imports .js because emit keeps specifiers unchanged.
// src/user.ts
export interface User { id: string; name: string }// src/main.ts
import { type User } from './user.js';.ts files but matches Node ESM runtime resolution.moduleResolution: NodeNext enforces this pattern.Related: ES Modules (import) - ESM rules
Express/Fastify handlers should return void or Promise<void> explicitly.
import type { Request, Response } from 'express';
export async function getHealth(_req: Request, res: Response): Promise<void> {
res.json({ status: 'ok' });
}any on req and res - define generics for body and params.Native tests with types - no Jest required for unit tests.
import { test } from 'node:test';
import assert from 'node:assert/strict';
function slugify(input: string): string {
return input.toLowerCase().replace(/\s+/g, '-');
}
test('slugify', () => {
assert.equal(slugify('Hello World'), 'hello-world');
});node --import tsx --test src/**/*.test.ts*.test.ts next to source or under src/__tests__.assert/strict throws on failure with clear diffs.dist/**/*.test.js after tsc for parity.strictNullChecks forces handling null and undefined.
function findUser(id: string, users: Map<string, { name: string }>): string {
const user = users.get(id);
if (!user) throw new Error(`User ${id} not found`);
return user.name;
}user?.name returns string | undefined - still narrow for business logic.! except in tests or after explicit guards.null should map to Result types or exceptions at boundaries.Related: Zod at Boundaries - runtime validation
Publish DTO types without server internals.
// packages/types/src/user.ts
export interface UserDto {
id: string;
displayName: string;
}// apps/api/src/handlers/user.ts
import type { UserDto } from '@acme/types/user';
export function toDto(user: { id: string; name: string }): UserDto {
return { id: user.id, displayName: user.name };
}exports in @acme/types/package.json for subpaths.Stack versions: This page was written for Node.js 24.18.0 (Active LTS), npm 10+, TypeScript 5.6+, Express 5, Fastify 5, and NestJS 11.
Reviewed by Chris St. John·Last updated Jul 16, 2026