Notre avis
Ce skill fournit des modèles de gestion d'erreurs robustes pour les applications TypeScript, incluant des classes d'erreurs personnalisées, un modèle Result, des limites d'erreur React et des wrappers asynchrones.
Points forts
- Couvre plusieurs approches (classes d'erreurs, Result, ErrorBoundary, tryCatch) adaptées à différents contextes.
- Les classes d'erreurs personnalisées incluent des codes et statuts HTTP pour une meilleure sémantique.
- Le modèle Result assure une gestion typée et sécurisée des succès et échecs.
- Intègre des limites d'erreur React pour les applications frontend.
Limites
- Certains modèles (ErrorBoundary) dépendent de React et des composants de classe.
- Le modèle Result peut alourdir le code avec des vérifications de type.
- Ne traite pas de stratégies avancées comme les retries ou les fallbacks.
Utilisez ce skill pour structurer la gestion d'erreurs dans des applications TypeScript complexes, notamment les API REST et les applications React.
Évitez ce skill pour des scripts simples ou des projets où une gestion d'erreurs légère suffit.
Analyse de sécurité
SûrThe skill provides educational code patterns for error handling in TypeScript. It does not include any destructive operations, obfuscated code, or instructions to execute external commands. The allowed tools are limited to safe file operations.
Aucun point d'attention détecté
Exemples
Create a custom error class NotFoundError that extends AppError with HTTP status code 404 and includes resource name and ID.Implement a Result type in TypeScript and use it for an async function that finds a user by ID, returning success or a NotFoundError.Write a React ErrorBoundary class component that catches errors, logs them, and renders a fallback UI with a role='alert' div showing the error message.name: error-handling-patterns description: Robust error handling patterns for TypeScript applications license: MIT compatibility: typescript 5+, nodejs 18+ allowed-tools: read_file write_file apply_patch search_with_context
Error Handling Patterns
Core Principles
- Fail fast - Detect and report errors early
- Fail safely - Errors shouldn't crash the application
- Fail informatively - Provide actionable error messages
- Recover gracefully - Handle expected failures
Custom Error Classes
// Base application error
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;
Error.captureStackTrace(this, this.constructor);
}
toJSON() {
return {
code: this.code,
message: this.message,
...(this.details && { details: this.details }),
};
}
}
// Specific error types
export class ValidationError extends AppError {
constructor(message: string, details?: Record<string, string>) {
super(message, 'VALIDATION_ERROR', 400, details);
}
}
export class NotFoundError extends AppError {
constructor(resource: string, id: string) {
super(`${resource} with id '${id}' not found`, 'NOT_FOUND', 404);
}
}
export class UnauthorizedError extends AppError {
constructor(message = 'Authentication required') {
super(message, 'UNAUTHORIZED', 401);
}
}
export class ForbiddenError extends AppError {
constructor(message = 'Insufficient permissions') {
super(message, 'FORBIDDEN', 403);
}
}
Result Type Pattern
type Result<T, E = Error> =
| { success: true; data: T }
| { success: false; error: E };
// Helper functions
function ok<T>(data: T): Result<T, never> {
return { success: true, data };
}
function err<E>(error: E): Result<never, E> {
return { success: false, error };
}
// Usage
async function findUser(id: string): Promise<Result<User, NotFoundError>> {
const user = await db.users.findUnique({ where: { id } });
if (!user) {
return err(new NotFoundError('User', id));
}
return ok(user);
}
// Consuming
const result = await findUser('123');
if (!result.success) {
console.error(result.error.message);
return;
}
const user = result.data; // Type-safe User
Error Boundaries (React)
import { Component, ErrorInfo, ReactNode } from 'react';
interface Props {
children: ReactNode;
fallback?: ReactNode;
onError?: (error: Error, info: ErrorInfo) => void;
}
interface State {
hasError: boolean;
error: Error | null;
}
export class ErrorBoundary extends Component<Props, State> {
state: State = { hasError: false, error: null };
static getDerivedStateFromError(error: Error): State {
return { hasError: true, error };
}
componentDidCatch(error: Error, info: ErrorInfo) {
this.props.onError?.(error, info);
}
render() {
if (this.state.hasError) {
return this.props.fallback ?? (
<div role="alert">
<h2>Something went wrong</h2>
<pre>{this.state.error?.message}</pre>
</div>
);
}
return this.props.children;
}
}
Async Error Handling
// Wrapper for async functions
function tryCatch<T>(
promise: Promise<T>
): Promise<[null, T] | [Error, null]> {
return promise
.then((data) => [null, data] as [null, T])
.catch((error) => [error as Error, null]);
}
// Usage
const [error, user] = await tryCatch(fetchUser(id));
if (error) {
handleError(error);
return;
}
// user is guaranteed to be defined here
// Multiple operations
async function processOrder(orderId: string) {
const [orderError, order] = await tryCatch(getOrder(orderId));
if (orderError) return err(orderError);
const [paymentError] = await tryCatch(processPayment(order));
if (paymentError) {
await tryCatch(rollbackOrder(order));
return err(paymentError);
}
return ok(order);
}
Retry Pattern
interface RetryOptions {
maxAttempts: number;
delayMs: number;
backoff?: 'linear' | 'exponential';
shouldRetry?: (error: Error) => boolean;
}
async function withRetry<T>(
fn: () => Promise<T>,
options: RetryOptions
): Promise<T> {
const { maxAttempts, delayMs, backoff = 'exponential', shouldRetry } = options;
let lastError: Error;
for (let attempt = 1; attempt <= maxAttempts; attempt++) {
try {
return await fn();
} catch (error) {
lastError = error as Error;
if (shouldRetry && !shouldRetry(lastError)) {
throw lastError;
}
if (attempt < maxAttempts) {
const delay = backoff === 'exponential'
? delayMs * Math.pow(2, attempt - 1)
: delayMs * attempt;
await sleep(delay);
}
}
}
throw lastError!;
}
// Usage
const data = await withRetry(() => fetchFromAPI(), {
maxAttempts: 3,
delayMs: 1000,
shouldRetry: (err) => err.message.includes('timeout'),
});
Validation Errors
import { z } from 'zod';
function validateInput<T>(schema: z.ZodSchema<T>, input: unknown): Result<T, ValidationError> {
const result = schema.safeParse(input);
if (!result.success) {
const details = result.error.flatten().fieldErrors;
return err(new ValidationError('Invalid input', details));
}
return ok(result.data);
}
// Usage
const userSchema = z.object({
email: z.string().email(),
age: z.number().min(18),
});
const result = validateInput(userSchema, req.body);
if (!result.success) {
return res.status(400).json(result.error.toJSON());
}
Logging Errors
interface ErrorContext {
userId?: string;
requestId?: string;
path?: string;
[key: string]: unknown;
}
function logError(error: Error, context: ErrorContext = {}) {
const payload = {
timestamp: new Date().toISOString(),
name: error.name,
message: error.message,
stack: error.stack,
...(error instanceof AppError && { code: error.code }),
...context,
};
console.error(JSON.stringify(payload));
// Send to error tracking service
if (process.env.NODE_ENV === 'production') {
// Sentry.captureException(error, { extra: context });
}
}
Best Practices
- Create specific error types for different failure modes
- Use Result types for expected failures (validation, not found)
- Throw errors for unexpected failures (bugs, system errors)
- Include context in error messages for debugging
- Log at boundaries - API handlers, event processors
- Validate at boundaries - external input, API responses
- Implement retry for transient failures
- Use error boundaries in React for graceful degradation
Expert Next.js App Router
Developpement
Un skill qui transforme Claude en expert Next.js App Router.
Générateur de README
Developpement
Crée des README.md professionnels et complets pour vos projets.
Rédacteur de Documentation API
Developpement
Génère de la documentation API complète au format OpenAPI/Swagger.