TypeScript 循环(保姆级教程)

TypeScript 循环:从基础到实战的完整指南

在编写程序时,重复执行某些操作是再常见不过的需求。比如你要处理一个用户列表,给每位用户发送欢迎消息;或者遍历一个数据数组,计算总和。这种“重复执行”的逻辑,就是编程中的“循环”。而 TypeScript 作为 JavaScript 的超集,提供了强大且类型安全的循环机制。今天我们就来深入聊聊 TypeScript 循环,帮助你从零开始掌握它。

TypeScript 循环不仅让你的代码更简洁,还能提升可读性和安全性。尤其当你结合类型系统使用时,循环中的变量错误会被编译时发现,避免运行时崩溃。接下来,我会用实际案例带你一步步掌握这些技巧。


常见的循环结构:for、while、do-while

TypeScript 支持三种基本的循环语法:forwhiledo-while。它们在功能上各有侧重,但核心思想都是“重复执行一段代码”。

for 循环:最常用的计数循环

for 循环是处理已知次数循环的首选。它的结构清晰,适合遍历数组、执行固定次数的操作。

// 示例:打印 1 到 5 的数字
for (let i = 1; i <= 5; i++) {
  console.log(`当前数字是:${i}`);
}

代码注释

  • let i = 1:定义循环变量 i,初始值为 1,代表当前循环次数。
  • i <= 5:循环继续的条件,只要 i 小于等于 5,就继续执行。
  • i++:每次循环结束后,i 自增 1。
  • console.log:输出当前值,用于观察执行过程。

这个结构就像一条自动运行的传送带:从 1 开始,每走一步就加 1,直到达到 5 就停下来。

while 循环:条件驱动的循环

while 循环更适合“不知道要循环多少次”的场景。只要条件为真,就一直执行。

// 示例:猜数字游戏(用户输入数字,直到猜对为止)
let secretNumber = 42;
let guess: number;

while (guess !== secretNumber) {
  guess = prompt("请输入一个数字:") as unknown as number;
  if (guess !== secretNumber) {
    console.log("猜错了,再试一次!");
  }
}
console.log("恭喜你,猜对了!");

代码注释

  • let guess: number:声明一个变量,用于存储用户输入。注意这里用 unknown as number 是为了处理 prompt 返回的字符串类型,实际开发中应做更安全的类型校验。
  • while (guess !== secretNumber):只要 guess 不等于 secretNumber,就继续循环。
  • prompt 是浏览器内置函数,返回字符串,需强制类型转换。
  • 一旦猜对,跳出循环,输出成功信息。

注意while 循环容易出现“死循环”——如果条件永远为真,程序将无限运行。因此一定要确保条件最终能变为 false。

do-while 循环:至少执行一次的循环

do-while 的特点是:先执行,再判断。也就是说,即使条件一开始为假,循环体也会执行一次。

// 示例:用户登录验证(至少尝试一次)
let username: string;
let password: string;

do {
  username = prompt("请输入用户名:") || "";
  password = prompt("请输入密码:") || "";
  if (username !== "admin" || password !== "123456") {
    console.log("用户名或密码错误,请重试。");
  }
} while (username !== "admin" || password !== "123456");

console.log("登录成功!");

代码注释

  • do { ... } while (...):先执行一次登录输入,再判断是否成功。
  • 即使用户第一次输入就错,也会提示错误,然后重新输入。
  • 适合需要“至少执行一次”的交互逻辑,比如表单验证。

💡 小贴士do-while 常用于命令行程序、登录流程、游戏主循环等场景。


遍历数组:for...of 与 forEach

在处理数据集合时,for...of 是 TypeScript 中最优雅的遍历方式。它专为可迭代对象设计,语法简洁,类型安全。

for...of:推荐用于数组和集合

// 示例:遍历用户数组并输出信息
const users: string[] = ["Alice", "Bob", "Charlie"];

for (const user of users) {
  console.log(`欢迎用户:${user}`);
}

代码注释

  • const user of users:每次循环取出 users 数组中的一个元素,赋值给 user。
  • const 保证变量不可变,避免意外修改。
  • 类型推断自动识别 user 为 string 类型,无需手动声明。

forEach:函数式编程风格的遍历

forEach 是数组的方法,适合执行副作用操作(如打印、发送请求)。

// 示例:使用 forEach 遍历并处理数据
const scores: number[] = [85, 92, 78, 96];

scores.forEach((score, index) => {
  console.log(`第 ${index + 1} 位同学的成绩是:${score}`);
});

代码注释

  • score:当前元素的值。
  • index:当前元素的索引(从 0 开始)。
  • => 是箭头函数,语法简洁,适合短函数。
  • 适合不需要返回值的遍历操作。

⚠️ 注意:forEach 不能用 breakcontinue 跳出循环。如果需要提前退出,应使用 for...offor 循环。


循环中的类型安全:TypeScript 的优势

TypeScript 的最大优势之一是类型检查。在循环中,类型信息能帮你避免很多低级错误。

// 示例:类型安全的循环处理对象数组
interface Product {
  id: number;
  name: string;
  price: number;
}

const products: Product[] = [
  { id: 1, name: "笔记本电脑", price: 5999 },
  { id: 2, name: "鼠标", price: 99 },
  { id: 3, name: "键盘", price: 299 }
];

// 正确使用类型推断
for (const product of products) {
  // TypeScript 自动识别 product 为 Product 类型
  console.log(`产品:${product.name},价格:${product.price} 元`);
  
  // 如果误写 product.prce,编译时就会报错!
  // error: Property 'prce' does not exist on type 'Product'
}

关键点

  • Product 是一个接口,定义了对象结构。
  • products: Product[] 声明了数组类型。
  • for (const product of products) 中,product 的类型被自动推断为 Product
  • 一旦写错字段名(如 prce),TypeScript 会在编译阶段报错,防止运行时崩溃。

实战案例:处理用户订单数据

让我们用一个完整的实战案例来巩固所学。假设你有一个订单系统,需要计算所有订单的总金额,并筛选出高价订单。

// 订单数据模型
interface Order {
  id: number;
  items: { name: string; price: number }[];
  status: "pending" | "shipped" | "delivered";
}

// 模拟订单数据
const orders: Order[] = [
  {
    id: 1001,
    items: [{ name: "T恤", price: 89 }, { name: "牛仔裤", price: 299 }],
    status: "shipped"
  },
  {
    id: 1002,
    items: [{ name: "耳机", price: 199 }],
    status: "pending"
  },
  {
    id: 1003,
    items: [{ name: "手机壳", price: 39 }, { name: "充电宝", price: 159 }],
    status: "delivered"
  }
];

// 计算所有订单的总金额
let totalAmount = 0;

for (const order of orders) {
  // 遍历每个订单的 items
  for (const item of order.items) {
    totalAmount += item.price;
  }
}

console.log(`所有订单的总金额为:${totalAmount} 元`);

// 筛选出金额超过 200 的订单
console.log("\n高价订单列表:");
for (const order of orders) {
  const orderTotal = order.items.reduce((sum, item) => sum + item.price, 0);
  if (orderTotal > 200) {
    console.log(`订单 ${order.id},总价:${orderTotal} 元`);
  }
}

代码注释

  • Order 接口定义了订单结构,包括 id、items 数组、status。
  • reduce 是数组方法,用于累加 items 的价格。
  • 外层循环遍历所有订单,内层循环处理每个订单的项目。
  • orderTotal > 200 用于筛选高价订单。
  • 输出清晰,逻辑分明,适合实际项目使用。

循环中的最佳实践与常见陷阱

✅ 推荐做法

  • 优先使用 for...of 遍历数组,语法简洁。
  • 避免在循环中频繁调用 pushsplice,可能影响性能。
  • 使用 const 声明循环变量,避免意外修改。
  • 利用 TypeScript 的类型系统,减少运行时错误。

❌ 常见错误

  • 死循环:忘记更新循环变量,如 for (let i = 0; i < 10; ) 忘记 i++
  • 类型错误:在 forEach 中误用变量类型,编译器无法发现。
  • 性能问题:在大数组中使用 for 循环时,避免在循环体内做耗时操作。

结语

TypeScript 循环是构建复杂逻辑的基础工具。无论是简单的计数、条件判断,还是处理复杂数据结构,掌握这些循环机制,都能让你的代码更高效、更安全。

forfor...of,从 whileforEach,每一种循环都有其适用场景。关键在于理解它们的本质,并在实际项目中灵活运用。

记住,编程不是记住语法,而是学会用合适的工具解决合适的问题。当你能熟练运用 TypeScript 循环,你就离写出高质量代码又近了一步。