Appearance
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 在不同类型项目中的应用:
- API 服务 - 展示了如何使用 TypeScript 构建类型安全的后端服务
- 状态管理 - 演示了如何创建类型安全的状态管理库
- 表单验证 - 介绍了如何实现类型安全的表单验证系统
- 设计模式 - 展示了如何使用 TypeScript 实现常见的设计模式
- 函数式编程 - 介绍了如何在 TypeScript 中使用函数式编程概念
通过这些案例,我们可以看到 TypeScript 如何帮助我们在实际项目中提高代码质量和可维护性。