Skip to content
Plurafy

TypeScript with Node.js

Using TypeScript for type-safe Node.js applications

Leverage TypeScript for better error detection and code maintainability in Node.js projects.

TypeScript adds static typing to JavaScript, catching errors at compile-time and improving developer experience.

Terminal window
npm init -y
npm install typescript @types/node --save-dev
npm install express
npm install --save-dev @types/express
# Initialize TypeScript
npx tsc --init
# Watch mode
npx tsc --watch
# Build and run
npm run build
node dist/index.js
// Primitives
const name: string = 'John';
const age: number = 30;
const isActive: boolean = true;
// Union types
let status: 'active' | 'inactive' = 'active';
// Array types
const numbers: number[] = [1, 2, 3];
const mixed: (string | number)[] = ['a', 1, 'b'];
// Tuple
const tuple: [string, number] = ['John', 30];
// Any and Unknown
let anything: any = 'can be anything';
let unknown_value: unknown = 'must check type';
if (typeof unknown_value === 'string') {
console.log(unknown_value.toUpperCase());
}
// Interface
interface User {
id: number;
name: string;
email: string;
active?: boolean; // Optional
}
// Type alias
type UserID = string | number;
// Function types
interface GetUser {
(id: number): Promise<User>;
}
const getUser: GetUser = async (id) => {
return { id, name: 'John', email: 'john@example.com' };
};
// Extending
interface Admin extends User {
role: 'admin' | 'moderator';
}
class Database {
private connection: Connection;
protected poolSize: number = 10;
public readonly name: string;
constructor(name: string) {
this.name = name;
}
public connect(): void {
this.connection = new Connection();
}
private createPool(): void {
// Private method
}
static getInstance(): Database {
return new Database('db');
}
}
// Abstract class
abstract class Animal {
abstract sound(): void;
move(): void {
console.log('Moving...');
}
}
class Dog extends Animal {
sound(): void {
console.log('Woof!');
}
}
import express, { Express, Request, Response } from 'express';
const app: Express = express();
app.use(express.json());
interface UserRequest extends Request {
user?: { id: number; email: string };
}
// Middleware with types
const authenticateToken = (
req: UserRequest,
res: Response,
next: Function
): void => {
const token = req.headers['authorization'];
if (!token) {
res.status(401).json({ error: 'No token' });
return;
}
// Verify token...
req.user = { id: 1, email: 'user@example.com' };
next();
};
// Typed routes
app.get('/users/:id', (req: Request, res: Response): void => {
const { id } = req.params;
res.json({ id, name: 'John' });
});
app.post('/users', (req: Request<{}, {}, User>, res: Response<User>): void => {
const user: User = req.body;
res.json(user);
});
app.get('/protected', authenticateToken, (req: UserRequest, res: Response): void => {
res.json({ user: req.user });
});
app.listen(3000, (): void => {
console.log('Server running on port 3000');
});
// Generic function
function getProperty<T, K extends keyof T>(obj: T, key: K): T[K] {
return obj[key];
}
// Generic class
class Queue<T> {
private items: T[] = [];
enqueue(item: T): void {
this.items.push(item);
}
dequeue(): T | undefined {
return this.items.shift();
}
}
// Usage
const numberQueue = new Queue<number>();
numberQueue.enqueue(5);
const stringQueue = new Queue<string>();
stringQueue.enqueue('hello');
// Enable in tsconfig.json: "experimentalDecoators": true
function Route(method: string, path: string) {
return function (target: any, propertyKey: string, descriptor: PropertyDescriptor) {
descriptor.value._route = { method, path };
return descriptor;
};
}
class UserController {
@Route('GET', '/users')
listUsers(req: Request, res: Response): void {
res.json([]);
}
}
// Class decorator
function Controller(path: string) {
return function <T extends { new(...args: any[]): {} }>(constructor: T) {
return class extends constructor {
basePath = path;
};
};
}
@Controller('/api')
class ProductController {
// Routes prefixed with /api
}
enum Status {
Active = 'active',
Inactive = 'inactive',
Pending = 'pending'
}
interface User {
id: number;
status: Status;
}
const user: User = {
id: 1,
status: Status.Active
};
enum Level {
Info = 0,
Warning = 1,
Error = 2
}
const logLevel: Level = Level.Info;
// ❌ Using any
const data: any = fetchData(); // Loses type checking
// ✅ Use unknown
const data: unknown = fetchData();
if (typeof data === 'object') {
// Now can access properties safely
}
// ❌ Over-use of interfaces
// ✅ Simple objects as type literals
type Config = { host: string; port: number };
// ❌ Mutable types
interface Point {
x: number;
y: number;
}
// ✅ Consider readonly
interface ReadonlyPoint {
readonly x: number;
readonly y: number;
}