clean-typescript-boundaries

Compare original and translation side by side

🇺🇸

Original

English
🇨🇳

Translation

Chinese

Clean Boundaries

清晰的边界处理

Boundary code converts uncertain external behavior into explicit internal contracts. Keep unsafe shapes at the edge and pass typed values inward.
边界代码将不确定的外部行为转换为明确的内部契约。将不安全的数据格式隔离在边缘,向内传递类型化的值。

B1: Validate At The Edge

B1: 在边界处验证

External data is
unknown
until validated. Parse and normalize once, then use domain types internally.
ts
// Bad - assertion lets invalid data into the system
const user = JSON.parse(responseText) as User;

// Good - boundary handles parsing, validation, and source context
const user = parseUserResponse(responseText);

function parseUserResponse(responseText: string): User {
  const payload = parseJson(responseText, "user response");
  return parseUser(payload);
}
Boundary parsers should reject invalid JSON, reject invalid shape, fill intentional defaults, and name the external source in errors.
外部数据在验证前属于
unknown
类型。仅解析和标准化一次,之后在内部使用领域类型。
ts
// Bad - assertion lets invalid data into the system
const user = JSON.parse(responseText) as User;

// Good - boundary handles parsing, validation, and source context
const user = parseUserResponse(responseText);

function parseUserResponse(responseText: string): User {
  const payload = parseJson(responseText, "user response");
  return parseUser(payload);
}
边界解析器应拒绝无效JSON、拒绝无效格式、填充预设默认值,并在错误信息中注明外部数据源。

B2: Make Configuration Typed

B2: 让配置类型化

Read environment variables, feature flags, and storage values once at startup or module boundaries. Convert strings into typed config before the rest of the app uses them.
在启动时或模块边界处一次性读取环境变量、功能标志和存储值。在应用其他部分使用之前,将字符串转换为类型化配置。

B3: Do Not Leak Vendor Types

B3: 避免暴露厂商类型

Wrap awkward SDKs and generated clients behind small adapters when their types or behavior should not shape the whole app.
ts
type PaymentReceipt = {
  id: string;
  totalCents: number;
};

interface Payments {
  charge(request: ChargeRequest): Promise<PaymentReceipt>;
}
Keep vendor-specific fields in the adapter unless the domain truly needs them.
当SDK或生成客户端的类型或行为不应影响整个应用时,用小型适配器封装这些难用的SDK和生成客户端。
ts
type PaymentReceipt = {
  id: string;
  totalCents: number;
};

interface Payments {
  charge(request: ChargeRequest): Promise<PaymentReceipt>;
}
除非领域确实需要,否则将厂商特定字段保留在适配器中。

B4: Boundary Tests

B4: 边界测试

Use focused tests around boundaries that are easy to misunderstand:
  • Parser rejects invalid external shapes.
  • Adapter maps SDK responses into domain types.
  • Missing config fails with a useful message.
  • Browser/storage fallbacks behave consistently.
针对容易被误解的边界进行针对性测试:
  • 解析器拒绝无效的外部数据格式。
  • 适配器将SDK响应映射为领域类型。
  • 缺失配置时返回有用的错误信息。
  • 浏览器/存储的回退行为一致。

Common Mistakes

常见错误

  • Letting
    any
    from a client library spread through application code.
  • Repeating JSON shape checks in many call sites.
  • Treating generated API models as domain models by default.
  • Hiding unsafe parsing behind helper names like
    getUser()
    without validation.
  • 让客户端库的
    any
    类型扩散到应用代码中。
  • 在多个调用点重复检查JSON格式。
  • 默认将生成的API模型当作领域模型使用。
  • 在没有验证的情况下,将不安全的解析隐藏在
    getUser()
    这类辅助函数背后。