
关于
跨 TypeScript、Python 和 Go 的健壮错误处理模式。涵盖类型化错误、错误边界、重试、熔断器和面向用户的错误消息。
name: error-handling description: 跨 TypeScript、Python 和 Go 的健壮错误处理模式。涵盖类型化错误、错误边界、重试、熔断器和面向用户的错误消息。 origin: ECC
错误处理模式
适用于生产应用的一致、健壮的错误处理模式。
何时激活
- 为新模块或服务设计错误类型或异常层次结构
- 为不可靠的外部依赖添加重试逻辑或熔断器
- 审查 API 端点中缺失的错误处理
- 实现面向用户的错误消息和反馈
- 调试级联故障或静默错误吞没
核心原则
- 快速且明确地失败 — 在错误发生的边界处暴露错误;不要掩埋它们
- 类型化错误优于字符串消息 — 错误是具有结构的一等值
- 用户消息 ≠ 开发者消息 — 向用户显示友好文本,在服务端记录完整上下文
- 永远不要静默吞没错误 — 每个
catch块必须处理、重新抛出或记录 - 错误是 API 契约的一部分 — 记录客户端可能收到的每个错误代码
TypeScript / JavaScript
类型化错误类
// Define an error hierarchy for your domain
export class AppError extends Error {
constructor(
message: string,
public readonly code: string,
public readonly statusCode: number = 500,
public readonly details?: unknown,
) {
super(message)
this.name = this.constructor.name
// Maintain correct prototype chain in transpiled ES5 JavaScript.
// Required for `instanceof` checks (e.g., `error instanceof NotFoundError`)
// to work correctly when extending the built-in Error class.
Object.setPrototypeOf(this, new.target.prototype)
}
}
export class NotFoundError extends AppError {
constructor(resource: string, id: string) {
super(`${resource} not found: ${id}`, 'NOT_FOUND', 404)
}
}
export class ValidationError extends AppError {
constructor(message: string, details: { field: string; message: string }[]) {
super(message, 'VALIDATION_ERROR', 422, details)
}
}
export class UnauthorizedError extends AppError {
constructor(reason = 'Authentication required') {
super(reason, 'UNAUTHORIZED', 401)
}
}
export class RateLimitError extends AppError {
constructor(public readonly retryAfterMs: number) {
super('Rate limit exceeded', 'RATE_LIMITED', 429)
}
}
Result 模式(无抛出风格)
用于失败是预期且常见的操作(解析、外部调用):
type Result<T, E = AppError> =
| { ok: true; value: T }
| { ok: false; error: E }
function ok<T>(value: T): Result<T> {
return { ok: true, value }
}
function err<E>(error: E): Result<never, E> {
return { ok: false, error }
}
// Usage
async function fetchUser(id: string): Promise<Result<User>> {
try {
const user = await db.users.findUnique({ where: { id } })
if (!user) return err(new NotFoundError('User', id))
return ok(user)
} catch (e) {
return err(new AppError('Database error', 'DB_ERROR'))
}
}
const result = await fetchUser('abc-123')
if (!result.ok) {
// TypeScript knows result.error here
logger.error('Failed to fetch user', { error: result.error })
return
}
// TypeScript knows result.value here
console.log(result.value.email)
API 错误处理器(Next.js / Express)
import { NextRequest, NextResponse } from 'next/server'
function handleApiError(error: unknown): NextResponse {
// Known application error
if (error instanceof AppError) {
return NextResponse.json(
{
error: {
code: error.code,
message: error.message,
...(error.details ? { details: error.details } : {}),
},
},
{ status: error.statusCode },
)
}
// Zod validation error
if (error instanceof z.ZodError) {
return NextResponse.json(
{
error: {
code: 'VALIDATION_ERROR',
message: 'Request validation failed',
details: error.issues.map(i => ({
field: i.path.join('.'),
message: i.message,
})),
},
},
{ status: 422 },
)
}
// Unexpected error — log details, return generic message
console.error('Unexpected error:', error)
return NextResponse.json(
{ error: { code: 'INTERNAL_ERROR', message: 'An unexpected error occurred' } },
{ status: 500 },
)
}
export async function POST(req: NextRequest) {
try {
// ... handler logic
} catch (error) {
return handleApiError(error)
}
}
React 错误边界
import { Component, ErrorInfo, ReactNode } from 'react'
interface Props {
fallback: ReactNode
onError?: (error: Error, info: ErrorInfo) => void
children: ReactNode
}
interface State {
hasError: boolean
error: Error | null
}
export class ErrorBoundary extends Component<Props, State> {
state: State = { hasE
兼容工具
Claude CodeCursor
标签
后端开发

