Working with TypeScript
1. Installing TypeScript Dependencies
2. Creating tsconfig.json Configuration
Example: tsconfig.json
{
"compilerOptions": {
"target": "ES2023",
"module": "NodeNext",
"moduleResolution": "NodeNext",
"outDir": "dist",
"rootDir": "src",
"strict": true,
"esModuleInterop": true,
"skipLibCheck": true,
"resolveJsonModule": true,
"verbatimModuleSyntax": true
},
"include": ["src/**/*"]
}
3. Typing Request and Response
Example: Typed handler
import type { Request, Response, NextFunction } from "express";
export const ping = (req: Request, res: Response) => {
res.json({ ok: true });
};
4. Creating Custom Request Types
Example: Module augmentation
// src/types/express.d.ts
import "express";
declare module "express-serve-static-core" {
interface Request {
id: string;
user?: { sub: string; role: "admin" | "user"; email: string };
}
}
5. Typing Middleware Functions
Example: RequestHandler
import type { RequestHandler } from "express";
export const requireAuth: RequestHandler = (req, res, next) => {
if (!req.user) return next(new HttpError(401));
next();
};
6. Using Generic Types
Example: Request<Params, ResBody, ReqBody, Query>
import type { Request, Response } from "express";
type Params = { id: string };
type Body = { email: string };
type Resp = { id: string; email: string };
type Query = { include?: string };
export const updateUser = async (
req: Request<Params, Resp, Body, Query>,
res: Response<Resp>
) => {
const user = await db.users.update(req.params.id, req.body);
res.json(user);
};
7. Creating Type-Safe Route Handlers
Example: Wrapper
import { z, ZodSchema } from "zod";
export const handler = <P, B, Q, R>(
schemas: { params?: ZodSchema<P>; body?: ZodSchema<B>; query?: ZodSchema<Q> },
fn: (input: { params: P; body: B; query: Q }) => Promise<R>
): RequestHandler => async (req, res, next) => {
try {
const input = {
params: schemas.params ? schemas.params.parse(req.params) : ({} as P),
body: schemas.body ? schemas.body.parse(req.body) : ({} as B),
query: schemas.query ? schemas.query.parse(req.query) : ({} as Q)
};
res.json(await fn(input));
} catch (err) { next(err); }
};
8. Typing Express Application
Example: Express type
import express, { type Express, type Router } from "express";
export function createApp(): Express {
const app = express();
app.use(express.json());
return app;
}
9. Using Type Guards
Example: Narrow error
export class HttpError extends Error {
constructor(public status: number, msg = "") { super(msg); }
}
export function isHttpError(e: unknown): e is HttpError {
return e instanceof HttpError;
}
10. Compiling TypeScript
| Tool | Use |
|---|---|
tsc | Type-check + emit JS |
tsx | Dev runner (no emit) RECOMMENDED |
ts-node | Older alternative |
swc / esbuild | Fast transpile (no type-check) |
Node 22.6+ --experimental-strip-types | Run .ts directly (preview) |