柳嘉希

硕士研究生毕业生

软件工程师 | 可扩展的API · 网络爬虫 · 数据集成 · Vibe代码清理专家

TypeScript 控制流、Record 字典与数组遍历:for、for...of、for...in、forEach、map 怎么选

这篇主要解决一个实际问题:当你面对对象、数组、字典和循环时,应该用哪种写法,为什么。

??|| 的区别

?? 只在值为 nullundefined 时使用默认值。

const value = input ?? "default";

|| 会在所有假值时使用默认值,包括 false0""NaNnullundefined

const page = query.page || 1;

如果 0 是合法值,不要用 || 做默认值。

const discount = userDiscount ?? 0;

Record 字典

Record<K, V> 表示一个 key-value 映射。

const userAges: Record<string, number> = {
  Alice: 25,
  Bob: 30,
};

如果 key 是固定集合,可以用联合类型限制。

type Role = "admin" | "editor" | "viewer";
 
const permissions: Record<Role, string[]> = {
  admin: ["read", "write", "delete"],
  editor: ["read", "write"],
  viewer: ["read"],
};

这比 Record<string, string[]> 更安全,因为少写一个 role 会直接报错。

索引签名

索引签名适合 key 完全动态的对象。

interface ScoreMap {
  [name: string]: number;
}
 
const scores: ScoreMap = {};
scores.Alice = 95;

缺点是它允许任意字符串 key,所以约束比联合类型弱。

循环怎么选

for 适合需要索引、需要提前退出、需要精细控制循环条件的场景。

for (let i = 0; i < items.length; i++) {
  if (items[i] === target) break;
}

for...of 适合遍历数组、字符串、Set 等可迭代对象,直接拿到值。

for (const fruit of fruits) {
  console.log(fruit);
}

for...in 适合遍历对象 key,但要注意原型链属性。

for (const key in person) {
  if (Object.prototype.hasOwnProperty.call(person, key)) {
    console.log(key, person[key as keyof typeof person]);
  }
}

forEach 与 map

forEach 用来执行副作用,不返回新数组。

numbers.forEach((num) => {
  console.log(num);
});

map 用来转换数组,并返回新数组。

const doubled = numbers.map((num) => num * 2);

简单规则:

  • 要生成新数组:用 map
  • 要过滤数组:用 filter
  • 要累计结果:用 reduce
  • 只是打印、调用函数、写日志:用 forEach
  • 需要 breakcontinue:用 for...offor