TypeScript 类型守卫

发布: (2026年4月6日 GMT+8 19:25)
7 分钟阅读
原文: Dev.to

Source: Dev.to

请提供您希望翻译的正文内容,我将为您翻译成简体中文。

当你构建支付系统时,“差不多就行”并不够好

单个 undefined 值或不匹配的对象属性可能决定交易是成功还是让客户感到沮丧(或导致失单)。

TypeScript 的类型守卫 是你的第一道防线。它们让你将宽泛、不确定的类型缩小为可以安全交互的特定类型。在本指南中,我们将构建一个迷你支付处理器,并学习如何使用类型守卫使其防崩溃。

1. 问题:JavaScript 的“静默失败”

想象一下,你有一个函数用于处理不同类型的支付响应。在普通的 JavaScript 中,你可能会这样写:

function processResponse(response) {
  // If response is a 'Success' object, it has a 'transactionId'
  // If it's an 'Error' object, it has a 'message'
  console.log("Payment successful! ID: " + response.transactionId);
}

// What if the API returned an error?
processResponse({ message: "Insufficient funds" });
// Output: "Payment successful! ID: undefined"

JavaScript 不会报错;它只会返回 undefined。这就是 静默失败。如果我们知道如何 缩窄 类型,TypeScript 可以帮助捕获它。

2. 内置守卫:基础

TypeScript 提供了内置的运算符来执行运行时检查。当 TypeScript 检测到这些检查时,它会 缩小 该代码块其余部分的类型。

typeof(检查原始类型)

在我们的支付应用中,amount 可能是 numberstring(如果它来自表单输入)。

function formatAmount(amount: string | number) {
  if (typeof amount === "string") {
    // TypeScript 知道此处 `amount` 是字符串。
    // 我们可以安全地调用字符串特有的方法。
    return parseFloat(amount).toFixed(2);
  }
  // 在这里 TypeScript 知道 `amount` 必须是数字。
  return amount.toFixed(2);
}

instanceof(检查类实例)

假设你有不同的类用于 CreditCardGiftCard 支付。每个类都有各自的验证逻辑。

class CreditCard {
  verifyCVV() {
    return true;
  }
}

class GiftCard {
  checkBalance() {
    return 50.0;
  }
}

function verifyPayment(method: CreditCard | GiftCard) {
  if (method instanceof CreditCard) {
    // 可以安全调用 CreditCard 的方法
    method.verifyCVV();
  } else {
    // 可以安全调用 GiftCard 的方法
    method.checkBalance();
  }
}

3. 自定义类型谓词: “守门员”

有时简单的检查并不足够。您可能需要一个可复用的函数来判断付款是否 complete(已完成)。这时 Custom Type Predicate 就派上用场了。

interface Payment {
  id: string;
  status: "pending" | "completed" | "failed";
}

interface CompletedPayment extends Payment {
  status: "completed";
  confirmedAt: Date;
}

/* This is our Custom Type Predicate */
function isCompleted(payment: Payment): payment is CompletedPayment {
  return payment.status === "completed";
}

const myPayment: Payment = { id: "123", status: "completed" };

if (isCompleted(myPayment)) {
  // TypeScript now knows `myPayment` has a `confirmedAt` property!
  console.log(myPayment.confirmedAt);
}

4. 判别联合:黄金标准

如果你只能从本文学到一种模式,就选这个。通过在你的类型中添加一个单一的公共属性(判别符),你可以获得 100 % 的类型安全和完美的 IDE 自动补全。

interface CardPayment {
  type: "card"; // Discriminator
  lastFour: string;
}

interface PayPalPayment {
  type: "paypal"; // Discriminator
  email: string;
}

type PaymentMethod = CardPayment | PayPalPayment;

function getReceipt(method: PaymentMethod) {
  switch (method.type) {
    case "card":
      return `Charged card ending in ${method.lastFour}`;
    case "paypal":
      return `Charged PayPal account: ${method.email}`;
  }
}

为什么这是一种超能力: 如果你后来添加了 Bitcoin 支付类型,却忘记更新 switch,TypeScript 会立即标记错误。这就像有一位高级开发者在你肩上监督一样。

5. 反模式:“前后对比”

作为初学者,你可能会倾向于使用快捷方式来消除红色错误提示。下面看看为什么不应该这样做。

“不安全的断言”

Before(“脏”做法):

// Bypassing safety with `as any`
const receipt = (payment as any).cardNumber;
// If `payment` is actually a Bank Transfer, this is `undefined`.

After(“防护”做法):

if ("cardNumber" in payment) {
  const receipt = payment.cardNumber; // Safe and verified
}

使用 in 运算符在处理来自外部 API 的不可预测数据时充当 盾牌

摘要:你的类型守卫决策树

不确定该使用哪种守卫?请参考以下检查表:

场景使用的守卫
简单原始类型(string、number、boolean 等)typeof
使用 new MyClass() 创建的对象instanceof
类型共享共同属性,如 typestatus判别联合(适用于复杂逻辑)
检查“混乱”对象上的特定属性in 运算符
你想要一个可复用的函数来简化 if 语句自定义类型谓词

使用正确的守卫,你的支付处理系统崩溃的可能性会大大降低——并且更有可能让客户满意。

谓词 (is)

结论

类型守卫弥合了 TypeScript 严格规则与 JavaScript 灵活现实之间的鸿沟。通过在支付系统中实现这些模式,你不仅仅是在编写代码——你正在构建一个可靠、可预测的引擎,轻松处理所有边缘情况。

祝编码愉快!

Reference:
Mastering TypeScript Type Guards — Better Stack Community

0 浏览
Back to Blog

相关文章

阅读更多 »

JavaScript 中的 Map 和 Set

什么是 Map?Map 是键‑值对的集合,类似于对象,但有几项改进:- 键可以是任何类型的对象、函数、原始值……