阿里云主机折上折
  • 微信号
您当前的位置:网站首页 > TypeScript支持与开发实践

TypeScript支持与开发实践

作者:陈川 阅读数:63440人阅读 分类: Node.js

TypeScript作为JavaScript的超集,在Express开发中提供了类型安全和现代工具链支持。结合Express框架,TypeScript能够显著提升后端代码的可维护性和开发效率,尤其在大型项目中表现突出。

TypeScript与Express的集成配置

在Express项目中使用TypeScript需要先安装必要的依赖:

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

基础tsconfig.json配置示例:

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

典型项目目录结构建议:

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

类型化路由处理

为路由处理器添加类型注解可以显著提升代码质量:

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);
    // 类型安全的参数访问
    const user = await userService.findById(userId);
    
    if (!user) {
      return res.status(404).json({ message: 'User not found' });
    }
    
    res.json(user);
  }
);

中间件的类型安全实现

创建类型化中间件示例:

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' });
  }
};

错误处理的类型化模式

实现类型安全的错误处理中间件:

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' });
});

数据库模型的类型定义

使用TypeScript接口定义Mongoose模型:

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);

请求验证的类型安全方案

使用zod进行运行时验证:

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
      });
      
      // 此时body已通过类型安全验证
      const newUser = await userService.create(body);
      res.status(201).json(newUser);
    } catch (err) {
      next(err);
    }
  }
);

依赖注入的模式实现

使用TypeScript装饰器实现控制器:

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);
  }
}

测试中的类型应用

使用Jest进行类型化测试:

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);
    
    // 类型断言
    const users: User[] = response.body;
    expect(users).toBeInstanceOf(Array);
    users.forEach(user => {
      expect(user).toHaveProperty('id');
      expect(user).toHaveProperty('name');
    });
  });
});

性能优化与类型提示

利用TypeScript的高级类型减少运行时检查:

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);
    }
  });
}

// 使用时获得自动补全
createRoute('POST', '/products', async (req, res) => {
  // 处理逻辑
});

环境变量的类型管理

强类型处理环境变量:

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);

// 现在可以安全访问
const port = env.PORT;  // 类型为number

高级路由类型技巧

使用泛型创建可复用路由类型:

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);
};

实时应用的类型处理

Socket.IO与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) => {
  // 现在有完整的类型提示
  socket.on('joinRoom', (roomId) => {
    socket.join(roomId);
  });
  
  socket.emit('message', 'Welcome!');
});

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

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

前端川

前端川,陈川的代码茶馆🍵,专治各种不服的Bug退散符💻,日常贩卖秃头警告级的开发心得🛠️,附赠一行代码笑十年的摸鱼宝典🐟,偶尔掉落咖啡杯里泡开的像素级浪漫☕。‌