clean-typescript-error-handling

Compare original and translation side by side

🇺🇸

Original

English
🇨🇳

Translation

Chinese

Clean Error Handling

优雅的错误处理

Error handling is part of the public behavior. Keep normal flow readable, preserve context, and make recoverable failures explicit.
错误处理是公共行为的一部分。要保持正常流程的可读性,保留上下文信息,并明确可恢复的异常情况。

EH1: Throw Useful Errors

EH1:抛出有价值的错误

Throw
Error
objects or project-specific subclasses. Do not throw strings or untyped objects.
ts
// Bad
throw "User not found";

// Good
throw new Error(`User not found: ${userId}`);
抛出
Error
对象或项目特定的子类。不要抛出字符串或无类型对象。
ts
// Bad
throw "User not found";

// Good
throw new Error(`User not found: ${userId}`);

EH2: Catch Unknown

EH2:捕获未知类型

Treat caught values as
unknown
and narrow before reading properties.
ts
try {
  await importUsers(file);
} catch (error: unknown) {
  if (error instanceof Error) {
    throw new Error(`Failed to import users from ${file}: ${error.message}`, {
      cause: error,
    });
  }

  throw new Error(`Failed to import users from ${file}`);
}
将捕获的值视为
unknown
类型,在读取属性前先进行类型收窄。
ts
try {
  await importUsers(file);
} catch (error: unknown) {
  if (error instanceof Error) {
    throw new Error(`Failed to import users from ${file}: ${error.message}`, {
      cause: error,
    });
  }

  throw new Error(`Failed to import users from ${file}`);
}

EH3: Do Not Swallow Failures

EH3:不要隐藏异常

Empty
catch
blocks hide system behavior. Either handle the failure, add context and throw an
Error
, or return a typed recoverable result.
ts
// Bad
try {
  await saveSettings(settings);
} catch {}

// Good
try {
  await saveSettings(settings);
} catch (error: unknown) {
  logger.error({ error }, "Failed to save settings");
  if (error instanceof Error) {
    throw new Error("Failed to save settings", { cause: error });
  }

  throw new Error("Failed to save settings");
}
空的
catch
块会掩盖系统行为。要么处理该异常,要么添加上下文信息后抛出
Error
,或者返回一个类型化的可恢复结果。
ts
// Bad
try {
  await saveSettings(settings);
} catch {}

// Good
try {
  await saveSettings(settings);
} catch (error: unknown) {
  logger.error({ error }, "Failed to save settings");
  if (error instanceof Error) {
    throw new Error("Failed to save settings", { cause: error });
  }

  throw new Error("Failed to save settings");
}

EH4: Null Is Not an Error Strategy

EH4:空值不是错误处理策略

Use
null
or
undefined
only when absence is an expected domain value. For recoverable failures, prefer a typed result when the project already uses that style.
ts
type ParseResult<T> =
  | { ok: true; value: T }
  | { ok: false; reason: string };
Do not introduce a Result abstraction into a codebase that consistently uses exceptions unless the boundary or domain clearly benefits from typed recovery.
仅当缺失是预期的领域值时才使用
null
undefined
。对于可恢复的异常,如果项目已采用类型化结果风格,则优先使用该方式。
ts
type ParseResult<T> =
  | { ok: true; value: T }
  | { ok: false; reason: string };
除非边界或领域明确能从类型化恢复中获益,否则不要在一贯使用异常处理的代码库中引入Result抽象。

Common Mistakes

常见错误

  • Catching and rethrowing without context or without narrowing
    unknown
    .
  • Returning
    undefined
    for both "not found" and "failed to load."
  • Logging an error and continuing with corrupted state.
  • Using broad fallbacks that make bugs look like valid empty data.
  • 捕获并重抛时未添加上下文信息,或未对
    unknown
    类型进行收窄。
  • 同时用
    undefined
    表示“未找到”和“加载失败”。
  • 记录错误后继续使用损坏的状态。
  • 使用宽泛的降级方案,导致Bug看起来像有效的空数据。