阿里云主机折上折
  • 微信号
Current Site:Index > TypeScript Support and Development Practices

TypeScript Support and Development Practices

Author:Chuan Chen 阅读数:63439人阅读 分类: Node.js

TypeScript, as a superset of JavaScript, provides type safety and modern toolchain support in Express development. When combined with the Express framework, TypeScript can significantly improve the maintainability and development efficiency of backend code, especially in large-scale projects.

Configuration for TypeScript and Express Integration

To use TypeScript in an Express project, the following dependencies must first be installed:

npm install express @types/express typescript --save-dev

Basic tsconfig.json configuration example:

{
  "compilerOptions": {
    "target": "ES2020",
    "module": "commonjs",
    "outDir": "./dist",
    "rootDir": "./src",
    "strict": true,
    "esModuleInterop": true,
    "skipLibCheck": true
  }
}

Recommended project directory structure:

project/
├── src/
│   ├── controllers/
│   ├── models/
│   ├── routes/
│   └── app.ts
├── dist/
├── tsconfig.json
└── package.json

Typed Route Handling

Adding type annotations to route handlers can significantly improve code quality:

import { Request, Response, NextFunction } from 'express';

interface User {
  id: number;
  name: string;
  email: string;
}

app.get('/users/:id', 
  async (req: Request<{ id: string }>, res: Response<User>, next: NextFunction) => {
    const userId = parseInt(req.params.id);
    // Type-safe parameter access
    const user = await userService.findById(userId);
    
    if (!user) {
      return res.status(404).json({ message: 'User not found' });
    }
    
    res.json(user);
  }
);

Type-Safe Middleware Implementation

Example of creating typed middleware:

interface AuthenticatedRequest extends Request {
  user?: {
    id: number;
    role: string;
  };
}

const authMiddleware = (
  req: AuthenticatedRequest, 
  res: Response, 
  next: NextFunction
) => {
  const token = req.headers.authorization?.split(' ')[1];
  
  if (!token) {
    return res.status(401).json({ error: 'Unauthorized' });
  }

  try {
    const payload = verifyToken(token);
    req.user = {
      id: payload.userId,
      role: payload.role
    };
    next();
  } catch (err) {
    res.status(403).json({ error: 'Invalid token' });
  }
};

Typed Error Handling Pattern

Implementing type-safe error handling middleware:

class AppError extends Error {
  constructor(
    public statusCode: number,
    public message: string,
    public details?: object
  ) {
    super(message);
  }
}

app.use((
  err: Error | AppError,
  req: Request,
  res: Response,
  next: NextFunction
) => {
  if (err instanceof AppError) {
    return res.status(err.statusCode).json({
      error: err.message,
      details: err.details
    });
  }

  console.error(err.stack);
  res.status(500).json({ error: 'Internal Server Error' });
});

Type Definitions for Database Models

Using TypeScript interfaces to define Mongoose models:

import { Document, Schema, model } from 'mongoose';

interface IProduct extends Document {
  name: string;
  price: number;
  stock: number;
  categories: string[];
}

const productSchema = new Schema<IProduct>({
  name: { type: String, required: true },
  price: { type: Number, min: 0 },
  stock: { type: Number, default: 0 },
  categories: { type: [String], index: true }
});

const Product = model<IProduct>('Product', productSchema);

Type-Safe Request Validation Solution

Using zod for runtime validation:

import { z } from 'zod';

const createUserSchema = z.object({
  body: z.object({
    name: z.string().min(2),
    email: z.string().email(),
    password: z.string().min(8)
  })
});

app.post('/users', 
  async (req: Request, res: Response, next: NextFunction) => {
    try {
      const { body } = createUserSchema.parse({
        body: req.body
      });
      
      // The body is now type-safe and validated
      const newUser = await userService.create(body);
      res.status(201).json(newUser);
    } catch (err) {
      next(err);
    }
  }
);

Dependency Injection Pattern Implementation

Using TypeScript decorators to implement controllers:

import { injectable } from 'inversify';
import { controller, httpGet } from 'inversify-express-utils';

interface IUserService {
  findAll(): Promise<User[]>;
}

@injectable()
@controller('/users')
class UserController {
  constructor(@inject('UserService') private userService: IUserService) {}

  @httpGet('/')
  async getAllUsers(req: Request, res: Response) {
    const users = await this.userService.findAll();
    res.json(users);
  }
}

Type Application in Testing

Using Jest for typed testing:

import request from 'supertest';
import app from '../src/app';

describe('User API', () => {
  it('GET /users should return user array', async () => {
    const response = await request(app)
      .get('/users')
      .expect(200);
    
    // Type assertion
    const users: User[] = response.body;
    expect(users).toBeInstanceOf(Array);
    users.forEach(user => {
      expect(user).toHaveProperty('id');
      expect(user).toHaveProperty('name');
    });
  });
});

Performance Optimization with Type Hints

Leveraging TypeScript's advanced types to reduce runtime checks:

type HTTPMethod = 'GET' | 'POST' | 'PUT' | 'DELETE' | 'PATCH';

function createRoute(
  method: HTTPMethod,
  path: string,
  handler: (req: Request, res: Response) => Promise<void>
) {
  app[method.toLowerCase()](path, async (req, res, next) => {
    try {
      await handler(req, res);
    } catch (err) {
      next(err);
    }
  });
}

// Autocomplete is available when using
createRoute('POST', '/products', async (req, res) => {
  // Processing logic
});

Type Management for Environment Variables

Strongly typing environment variables:

import dotenv from 'dotenv';
import { z } from 'zod';

dotenv.config();

const envSchema = z.object({
  PORT: z.string().transform(Number),
  DATABASE_URL: z.string().url(),
  NODE_ENV: z.enum(['development', 'production', 'test'])
});

const env = envSchema.parse(process.env);

// Now safe to access
const port = env.PORT;  // Type is number

Advanced Routing Type Techniques

Using generics to create reusable route types:

type TypedRouteHandler<
  P = {},
  ResBody = {},
  ReqBody = {},
  Query = {}
> = (
  req: Request<P, ResBody, ReqBody, Query>,
  res: Response<ResBody>,
  next: NextFunction
) => Promise<void> | void;

const getUserHandler: TypedRouteHandler<
  { id: string },
  User,
  {},
  { include: string }
> = async (req, res) => {
  const user = await userService.findById(req.params.id, {
    include: req.query.include
  });
  res.json(user);
};

Type Handling for Real-Time Applications

Socket.IO integration example with TypeScript:

import { Server } from 'socket.io';

interface ServerToClientEvents {
  message: (content: string) => void;
  userConnected: (userId: number) => void;
}

interface ClientToServerEvents {
  joinRoom: (roomId: string) => void;
  sendMessage: (content: string) => void;
}

const io = new Server<ClientToServerEvents, ServerToClientEvents>(httpServer);

io.on('connection', (socket) => {
  // Now with full type hints
  socket.on('joinRoom', (roomId) => {
    socket.join(roomId);
  });
  
  socket.emit('message', 'Welcome!');
});

本站部分内容来自互联网,一切版权均归源网站或源作者所有。

如果侵犯了你的权益请来信告知我们删除。邮箱:cc@cccx.cn

Front End Chuan

Front End Chuan, Chen Chuan's Code Teahouse 🍵, specializing in exorcising all kinds of stubborn bugs 💻. Daily serving baldness-warning-level development insights 🛠️, with a bonus of one-liners that'll make you laugh for ten years 🐟. Occasionally drops pixel-perfect romance brewed in a coffee cup ☕.