Skip to content
On this page

TypeScript 实战案例

通过实际项目案例来演示如何在真实场景中使用 TypeScript。

案例一:REST API 服务

项目结构

api-service/
├── src/
│   ├── models/
│   │   ├── User.ts
│   │   └── Post.ts
│   ├── interfaces/
│   │   ├── IUser.ts
│   │   └── IPost.ts
│   ├── controllers/
│   │   ├── UserController.ts
│   │   └── PostController.ts
│   ├── middleware/
│   │   └── auth.ts
│   ├── routes/
│   │   ├── userRoutes.ts
│   │   └── postRoutes.ts
│   ├── types/
│   │   └── express.d.ts
│   ├── utils/
│   │   └── validation.ts
│   └── server.ts
├── package.json
└── tsconfig.json

类型定义

typescript
// src/interfaces/IUser.ts
export interface IUser {
  id: number;
  username: string;
  email: string;
  password: string;
  createdAt: Date;
  updatedAt: Date;
  isActive: boolean;
}

export interface CreateUserDto {
  username: string;
  email: string;
  password: string;
}

export interface UpdateUserDto {
  username?: string;
  email?: string;
  isActive?: boolean;
}

export interface LoginCredentials {
  email: string;
  password: string;
}

export interface AuthenticatedUser {
  id: number;
  username: string;
  email: string;
}
typescript
// src/interfaces/IPost.ts
export interface IPost {
  id: number;
  title: string;
  content: string;
  authorId: number;
  publishedAt?: Date;
  createdAt: Date;
  updatedAt: Date;
  tags: string[];
}

export interface CreatePostDto {
  title: string;
  content: string;
  tags?: string[];
}

export interface UpdatePostDto {
  title?: string;
  content?: string;
  tags?: string[];
}

数据模型

typescript
// src/models/User.ts
import { IUser, CreateUserDto, UpdateUserDto } from '../interfaces/IUser';

export class User implements IUser {
  id: number;
  username: string;
  email: string;
  password: string;
  createdAt: Date;
  updatedAt: Date;
  isActive: boolean;

  constructor(data: Partial<IUser>) {
    this.id = data.id ?? 0;
    this.username = data.username ?? '';
    this.email = data.email ?? '';
    this.password = data.password ?? '';
    this.createdAt = data.createdAt ?? new Date();
    this.updatedAt = data.updatedAt ?? new Date();
    this.isActive = data.isActive ?? true;
  }

  static fromCreateDto(dto: CreateUserDto): User {
    const user = new User({
      username: dto.username,
      email: dto.email,
      password: dto.password,
      createdAt: new Date(),
      updatedAt: new Date(),
      isActive: true
    });
    return user;
  }

  updateFromDto(dto: UpdateUserDto): void {
    if (dto.username) this.username = dto.username;
    if (dto.email) this.email = dto.email;
    if (dto.isActive !== undefined) this.isActive = dto.isActive;
    this.updatedAt = new Date();
  }
}

控制器

typescript
// src/controllers/UserController.ts
import { Request, Response } from 'express';
import { User } from '../models/User';
import { 
  CreateUserDto, 
  UpdateUserDto, 
  LoginCredentials 
} from '../interfaces/IUser';

export class UserController {
  private users: User[] = [];
  private nextId = 1;

  async register(req: Request<{}, {}, CreateUserDto>, res: Response) {
    try {
      const { username, email, password } = req.body;
      
      // 验证输入
      if (!username || !email || !password) {
        return res.status(400).json({ error: 'Missing required fields' });
      }

      // 检查用户是否已存在
      const existingUser = this.users.find(u => u.email === email);
      if (existingUser) {
        return res.status(409).json({ error: 'User already exists' });
      }

      // 创建新用户
      const newUser = new User({
        id: this.nextId++,
        username,
        email,
        password, // 实际应用中应该加密密码
        createdAt: new Date(),
        updatedAt: new Date(),
        isActive: true
      });

      this.users.push(newUser);
      res.status(201).json({ user: newUser });
    } catch (error) {
      res.status(500).json({ error: 'Internal server error' });
    }
  }

  async login(req: Request<{}, {}, LoginCredentials>, res: Response) {
    try {
      const { email, password } = req.body;
      
      const user = this.users.find(u => u.email === email && u.password === password);
      if (!user) {
        return res.status(401).json({ error: 'Invalid credentials' });
      }

      // 在实际应用中,这里应该生成 JWT token
      res.json({
        user: { id: user.id, username: user.username, email: user.email },
        token: 'fake-jwt-token'
      });
    } catch (error) {
      res.status(500).json({ error: 'Internal server error' });
    }
  }

  async getAll(req: Request, res: Response) {
    try {
      res.json({ users: this.users });
    } catch (error) {
      res.status(500).json({ error: 'Internal server error' });
    }
  }

  async getById(req: Request<{ id: string }>, res: Response) {
    try {
      const id = parseInt(req.params.id);
      const user = this.users.find(u => u.id === id);
      
      if (!user) {
        return res.status(404).json({ error: 'User not found' });
      }

      res.json({ user });
    } catch (error) {
      res.status(500).json({ error: 'Internal server error' });
    }
  }

  async update(req: Request<{ id: string }, {}, UpdateUserDto>, res: Response) {
    try {
      const id = parseInt(req.params.id);
      const userIndex = this.users.findIndex(u => u.id === id);
      
      if (userIndex === -1) {
        return res.status(404).json({ error: 'User not found' });
      }

      const updates = req.body;
      Object.assign(this.users[userIndex], updates, { updatedAt: new Date() });

      res.json({ user: this.users[userIndex] });
    } catch (error) {
      res.status(500).json({ error: 'Internal server error' });
    }
  }

  async delete(req: Request<{ id: string }>, res: Response) {
    try {
      const id = parseInt(req.params.id);
      const userIndex = this.users.findIndex(u => u.id === id);
      
      if (userIndex === -1) {
        return res.status(404).json({ error: 'User not found' });
      }

      this.users.splice(userIndex, 1);
      res.status(204).send();
    } catch (error) {
      res.status(500).json({ error: 'Internal server error' });
    }
  }
}

Express 类型扩展

typescript
// src/types/express.d.ts
import { IUser } from '../interfaces/IUser';

declare global {
  namespace Express {
    interface Request {
      user?: IUser;
    }
  }
}

路由定义

typescript
// src/routes/userRoutes.ts
import { Router } from 'express';
import { UserController } from '../controllers/UserController';

const router = Router();
const userController = new UserController();

router.post('/register', (req, res) => userController.register(req, res));
router.post('/login', (req, res) => userController.login(req, res));
router.get('/', (req, res) => userController.getAll(req, res));
router.get('/:id', (req, res) => userController.getById(req, res));
router.put('/:id', (req, res) => userController.update(req, res));
router.delete('/:id', (req, res) => userController.delete(req, res));

export default router;

主服务器文件

typescript
// src/server.ts
import express, { Application } from 'express';
import userRoutes from './routes/userRoutes';

const app: Application = express();
const PORT = process.env.PORT || 3000;

// 中间件
app.use(express.json());

// 路由
app.use('/api/users', userRoutes);

// 错误处理中间件
app.use((err: Error, req: express.Request, res: express.Response, next: express.NextFunction) => {
  console.error(err.stack);
  res.status(500).json({ error: 'Something went wrong!' });
});

// 启动服务器
app.listen(PORT, () => {
  console.log(`Server is running on port ${PORT}`);
});

案例二:状态管理库

简单的状态管理实现

typescript
// src/state/Store.ts
export interface Action<T = any> {
  type: string;
  payload?: T;
}

export interface Reducer<S, A extends Action = Action> {
  (state: S | undefined, action: A): S;
}

export interface Store<S = any, A extends Action = Action> {
  getState(): S;
  dispatch(action: A): A;
  subscribe(listener: () => void): () => void;
}

export interface StoreEnhancer<S = any, A extends Action = Action> {
  (createStore: StoreCreator<S, A>): StoreCreator<S, A>;
}

export interface StoreCreator<S = any, A extends Action = Action> {
  (reducer: Reducer<S, A>, initialState?: S, enhancer?: StoreEnhancer<S, A>): Store<S, A>;
}

export function createStore<S, A extends Action = Action>(
  reducer: Reducer<S, A>,
  initialState?: S,
  enhancer?: StoreEnhancer<S, A>
): Store<S, A> {
  if (enhancer) {
    return enhancer(createStore)(reducer, initialState);
  }

  let currentState: S = initialState as S;
  let currentListener: (() => void) | null = null;

  function getState(): S {
    return currentState;
  }

  function dispatch(action: A): A {
    currentState = reducer(currentState, action);
    if (currentListener) {
      currentListener();
    }
    return action;
  }

  function subscribe(listener: () => void): () => void {
    currentListener = listener;
    return () => {
      currentListener = null;
    };
  }

  // 初始化状态
  dispatch({ type: '@@INIT' } as A);

  return { getState, dispatch, subscribe };
}

类型安全的状态管理

typescript
// src/state/TodoStore.ts
import { Action, createStore, Store } from './Store';

// 定义状态类型
export interface Todo {
  id: number;
  text: string;
  completed: boolean;
}

export interface TodoState {
  todos: Todo[];
  visibilityFilter: 'SHOW_ALL' | 'SHOW_COMPLETED' | 'SHOW_ACTIVE';
}

// 定义动作类型
export const ADD_TODO = 'ADD_TODO';
export const TOGGLE_TODO = 'TOGGLE_TODO';
export const SET_VISIBILITY_FILTER = 'SET_VISIBILITY_FILTER';

interface AddTodoAction extends Action<typeof ADD_TODO> {
  payload: {
    id: number;
    text: string;
  };
}

interface ToggleTodoAction extends Action<typeof TOGGLE_TODO> {
  payload: {
    id: number;
  };
}

interface SetVisibilityFilterAction extends Action<typeof SET_VISIBILITY_FILTER> {
  payload: TodoState['visibilityFilter'];
}

export type TodoAction = 
  | AddTodoAction 
  | ToggleTodoAction 
  | SetVisibilityFilterAction;

// 定义 reducer
const todoReducer = (
  state: TodoState = {
    todos: [],
    visibilityFilter: 'SHOW_ALL'
  },
  action: TodoAction
): TodoState => {
  switch (action.type) {
    case ADD_TODO:
      return {
        ...state,
        todos: [
          ...state.todos,
          {
            id: action.payload.id,
            text: action.payload.text,
            completed: false
          }
        ]
      };
    case TOGGLE_TODO:
      return {
        ...state,
        todos: state.todos.map(todo =>
          todo.id === action.payload.id
            ? { ...todo, completed: !todo.completed }
            : todo
        )
      };
    case SET_VISIBILITY_FILTER:
      return {
        ...state,
        visibilityFilter: action.payload
      };
    default:
      return state;
  }
};

// 创建 store
export const todoStore: Store<TodoState, TodoAction> = createStore(todoReducer);

// 动作创建器
export const addTodo = (text: string): AddTodoAction => ({
  type: ADD_TODO,
  payload: { id: Date.now(), text }
});

export const toggleTodo = (id: number): ToggleTodoAction => ({
  type: TOGGLE_TODO,
  payload: { id }
});

export const setVisibilityFilter = (filter: TodoState['visibilityFilter']): SetVisibilityFilterAction => ({
  type: SET_VISIBILITY_FILTER,
  payload: filter
});

案例三:类型安全的表单验证

typescript
// src/validation/FormValidator.ts
export interface ValidationRule<T> {
  (value: T): boolean | string;
}

export interface FieldValidator<T> {
  required?: boolean;
  rules?: ValidationRule<T>[];
  message?: string;
}

export interface FormSchema<T> {
  [key: string]: FieldValidator<any>;
}

export interface ValidationResult<T> {
  isValid: boolean;
  errors: Partial<Record<keyof T, string>>;
}

export class FormValidator<T extends Record<string, any>> {
  constructor(private schema: FormSchema<T>) {}

  validate(data: T): ValidationResult<T> {
    const errors: Partial<Record<keyof T, string>> = {};

    for (const fieldName in this.schema) {
      if (this.schema.hasOwnProperty(fieldName)) {
        const fieldValidator = this.schema[fieldName];
        const fieldValue = data[fieldName];

        // 检查必填字段
        if (fieldValidator.required && (fieldValue === undefined || fieldValue === null || fieldValue === '')) {
          errors[fieldName as keyof T] = fieldValidator.message || `${fieldName} is required`;
          continue;
        }

        // 应用自定义规则
        if (fieldValidator.rules && fieldValidator.rules.length > 0) {
          for (const rule of fieldValidator.rules) {
            const result = rule(fieldValue);
            if (typeof result === 'string') {
              errors[fieldName as keyof T] = result;
              break;
            } else if (result === false) {
              errors[fieldName as keyof T] = fieldValidator.message || `${fieldName} is invalid`;
              break;
            }
          }
        }
      }
    }

    return {
      isValid: Object.keys(errors).length === 0,
      errors
    };
  }
}

// 使用示例
interface LoginForm {
  email: string;
  password: string;
}

const emailRegex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;

const loginFormValidator = new FormValidator<LoginForm>({
  email: {
    required: true,
    rules: [
      (value: string) => emailRegex.test(value) || 'Invalid email format'
    ],
    message: 'Email is required'
  },
  password: {
    required: true,
    rules: [
      (value: string) => value.length >= 8 || 'Password must be at least 8 characters'
    ],
    message: 'Password is required'
  }
});

// 验证表单
const formData: LoginForm = {
  email: 'test@example.com',
  password: '12345678'
};

const result = loginFormValidator.validate(formData);
console.log(result.isValid); // true or false
console.log(result.errors);  // 错误信息

案例四:TypeScript 与设计模式

观察者模式的类型安全实现

typescript
// src/patterns/Observer.ts
export interface Observer<T> {
  update(data: T): void;
}

export interface Subject<T> {
  attach(observer: Observer<T>): void;
  detach(observer: Observer<T>): void;
  notify(data: T): void;
}

export class NewsAgency<T> implements Subject<T> {
  private observers: Observer<T>[] = [];

  attach(observer: Observer<T>): void {
    this.observers.push(observer);
  }

  detach(observer: Observer<T>): void {
    const index = this.observers.indexOf(observer);
    if (index > -1) {
      this.observers.splice(index, 1);
    }
  }

  notify(data: T): void {
    this.observers.forEach(observer => observer.update(data));
  }
}

export class NewsChannel<T> implements Observer<T> {
  constructor(private name: string) {}

  update(news: T): void {
    console.log(`${this.name} received news:`, news);
  }
}

// 使用示例
interface NewsItem {
  headline: string;
  content: string;
  timestamp: Date;
}

const newsAgency = new NewsAgency<NewsItem>();
const channel1 = new NewsChannel<NewsItem>('Channel 1');
const channel2 = new NewsChannel<NewsItem>('Channel 2');

newsAgency.attach(channel1);
newsAgency.attach(channel2);

const breakingNews: NewsItem = {
  headline: 'Breaking News!',
  content: 'This is breaking news',
  timestamp: new Date()
};

newsAgency.notify(breakingNews);

案例五:TypeScript 与函数式编程

typescript
// src/functional/FunctionalUtils.ts
export type Predicate<T> = (value: T) => boolean;
export type Mapper<T, U> = (value: T) => U;
export type Reducer<T, U> = (acc: U, value: T) => U;

// Maybe 类型(类似 Option/Maybe monad)
export type Maybe<T> = T | null | undefined;

export const Maybe = {
  map: <T, U>(value: Maybe<T>, fn: Mapper<T, U>): Maybe<U> => {
    if (value == null) return null;
    return fn(value);
  },

  chain: <T, U>(value: Maybe<T>, fn: (value: T) => Maybe<U>): Maybe<U> => {
    if (value == null) return null;
    return fn(value);
  },

  filter: <T>(value: Maybe<T>, predicate: Predicate<T>): Maybe<T> => {
    if (value == null) return null;
    return predicate(value) ? value : null;
  }
};

// Either 类型(类似 Result/Either monad)
export type Either<L, R> = { left: L } | { right: R };

export const Either = {
  left: <L, R>(value: L): Either<L, R> => ({ left: value }),
  right: <L, R>(value: R): Either<L, R> => ({ right: value }),
  
  map: <L, R, R2>(either: Either<L, R>, fn: Mapper<R, R2>): Either<L, R2> => {
    if ('left' in either) return either;
    return { right: fn(either.right) };
  },
  
  mapLeft: <L, L2, R>(either: Either<L, R>, fn: Mapper<L, L2>): Either<L2, R> => {
    if ('right' in either) return either;
    return { left: fn(either.left) };
  }
};

// 使用示例
const safeDivide = (a: number, b: number): Either<string, number> => {
  if (b === 0) {
    return Either.left('Division by zero');
  }
  return Either.right(a / b);
};

const result1 = safeDivide(10, 2); // Right(5)
const result2 = safeDivide(10, 0); // Left('Division by zero')

console.log(Either.map(result1, x => x * 2)); // Right(10)
console.log(Either.map(result2, x => x * 2)); // Left('Division by zero')

小结

这些实战案例展示了 TypeScript 在不同类型项目中的应用:

  1. API 服务 - 展示了如何使用 TypeScript 构建类型安全的后端服务
  2. 状态管理 - 演示了如何创建类型安全的状态管理库
  3. 表单验证 - 介绍了如何实现类型安全的表单验证系统
  4. 设计模式 - 展示了如何使用 TypeScript 实现常见的设计模式
  5. 函数式编程 - 介绍了如何在 TypeScript 中使用函数式编程概念

通过这些案例,我们可以看到 TypeScript 如何帮助我们在实际项目中提高代码质量和可维护性。