迭代器与生成器
本教程共 80 篇 · 第 72 篇 · 更新于 2026-08-10 · 约 14 分钟阅读
本节目标:搞懂 TypeScript 世界里的”可迭代”是什么意思——Iterable 和 Iterator 接口长什么样、for..of 的类型推断有多聪明、生成器函数的三个泛型参数到底是干嘛的。
可迭代协议:Iterable 与 Iterator
ES2015 引入了一套”可迭代协议”——一个对象只要实现了 Symbol.iterator 方法,就能被 for..of 遍历。TypeScript 为这套协议提供了完整的类型定义:
// TypeScript 内置的迭代器接口(简化版)
interface Iterator<T> {
next(): IteratorResult<T>;
}
interface IteratorResult<T> {
done: boolean;
value: T;
}
interface Iterable<T> {
[Symbol.iterator](): Iterator<T>;
}
简单说:Iterable 是”可被迭代的容器”,Iterator 是”正在迭代的游标”。数组、Map、Set、字符串都内置了 Symbol.iterator 方法,所以它们都是 Iterable。
const arr = [1, 2, 3];
const it = arr[Symbol.iterator]();
console.log(it.next()); // { value: 1, done: false }
console.log(it.next()); // { value: 2, done: false }
console.log(it.next()); // { value: 3, done: false }
console.log(it.next()); // { value: undefined, done: true }
TypeScript 能完美推断 it 的类型为 Iterator<number>,next() 返回 IteratorResult<number>。
for..of 的类型推断
当你在 TypeScript 里写 for..of,编译器会根据 Iterable 的泛型参数自动推断循环变量的类型:
const names = ["Alice", "Bob", "Charlie"];
for (const name of names) {
// name 被推断为 string,不需要手动标注
console.log(name.toUpperCase());
}
Map 和 Set 的推断同样精准:
const map = new Map<string, number>();
map.set("a", 1);
map.set("b", 2);
for (const [key, value] of map) {
// key 推断为 string,value 推断为 number
console.log(`${key}: ${value}`);
}
const set = new Set([10, 20, 30]);
for (const num of set) {
// num 推断为 number
console.log(num * 2);
}
Tip想让你的自定义类支持
for..of?实现[Symbol.iterator]()方法就行,TypeScript 会自动识别。
自定义可迭代对象
直接实现 Iterable<T> 接口:
class Range implements Iterable<number> {
constructor(private start: number, private end: number) {}
[Symbol.iterator](): Iterator<number> {
let current = this.start;
const end = this.end;
return {
next(): IteratorResult<number> {
if (current <= end) {
return { value: current++, done: false };
}
return { value: undefined as any, done: true };
},
};
}
}
const range = new Range(1, 5);
for (const n of range) {
console.log(n); // 1, 2, 3, 4, 5
}
class Range implements Iterable<number> 告诉 TS:“这个类的实例可以被迭代,每次迭代产生 number”。如果你漏写了 next() 方法,TS 会报错,因为 Iterator<number> 要求返回 IteratorResult<number>。
生成器函数
生成器(Generator)是创建迭代器的最简方式。用 function* 声明,yield 暂停执行:
function* simpleGenerator() {
yield 1;
yield 2;
yield 3;
}
const gen = simpleGenerator();
console.log(gen.next()); // { value: 1, done: false }
console.log(gen.next()); // { value: 2, done: false }
console.log(gen.next()); // { value: 3, done: false }
console.log(gen.next()); // { value: undefined, done: true }
TypeScript 会自动推断生成器的返回类型。上例中 gen 的类型是 Generator<number, void, unknown>。
Generator 的三个泛型参数
生成器函数的类型签名是 Generator<T, TReturn, TNext>:
| 参数 | 含义 | 对应什么 |
|---|---|---|
T | yield 产生的值的类型 | next() 返回的 value |
TReturn | return 返回的最终结果类型 | 最后 { done: true, value: ... } 的 value |
TNext | 通过 next(x) 传入的值类型 | yield 表达式的返回值类型 |
大多数时候你只关心第一个参数:
function* numberGenerator(): Generator<number> {
yield 1;
yield 2;
yield 3;
}
Generator<number> 等价于 Generator<number, void, unknown>。TReturn 和 TNext 用了默认值。
TReturn:return 的最终值
生成器可以用 return 提前结束,返回的值会在 done: true 时作为 value:
function* genWithReturn(): Generator<number, string> {
yield 1;
yield 2;
return "done";
}
const g = genWithReturn();
console.log(g.next()); // { value: 1, done: false }
console.log(g.next()); // { value: 2, done: false }
console.log(g.next()); // { value: "done", done: true }
// ↑ 注意:final value 是 string,不是 number
如果没有显式 return,TReturn 默认是 void。迭代到最后 done: true 时 value 为 undefined。
TNext:向生成器”塞值”
你可以通过 next(x) 向生成器内部传值,yield 表达式会收到这个值:
function* accumulator(): Generator<number, void, number> {
let sum = 0;
// 第一个 next() 传入的值会被忽略(启动时还没有暂停点)
const add1: number = (yield sum) as number;
sum += add1;
const add2: number = (yield sum) as number;
sum += add2;
yield sum;
}
const acc = accumulator();
console.log(acc.next()); // { value: 0, done: false } —— 启动,走到第一个 yield
console.log(acc.next(5)); // { value: 5, done: false } —— sum += 5
console.log(acc.next(3)); // { value: 8, done: false } —— sum += 3
console.log(acc.next()); // { value: undefined, done: true }
Note第一次
next()不能传值(或者传了也会被忽略),因为生成器还没执行到任何yield暂停点。这是 JS 的运行时行为,TS 类型系统没法强制检查这一点。
生成器作为 Iterable
生成器返回的对象同时实现了 Iterable 接口,所以可以直接用在 for..of 里:
function* alphabet() {
yield "a";
yield "b";
yield "c";
}
for (const letter of alphabet()) {
console.log(letter); // "a", "b", "c"
}
TypeScript 推断 letter 为 string,来自生成器的 T 参数。
yield* 委托给另一个迭代器
yield* 把迭代委托给另一个 Iterable 对象:
function* combined() {
yield* [1, 2, 3];
yield* new Set(["a", "b"]);
}
for (const item of combined()) {
// item 的类型推断为 number | string
console.log(item);
}
注意 TS 的推断:它把两个 yield* 的产出类型合并成联合类型 number | string。
如果你明确标注返回类型,可以用更精确的类型:
function* numbered(): Generator<number | string> {
yield* [1, 2, 3];
yield* ["a", "b", "c"];
}
异步生成器
async function* 创建的异步生成器,用 for await..of 遍历:
async function* asyncNumbers() {
for (let i = 0; i < 3; i++) {
await new Promise((r) => setTimeout(r, 100));
yield i;
}
}
const asyncGen = asyncNumbers();
// asyncGen 的类型是 AsyncGenerator<number, void, unknown>
(async () => {
for await (const num of asyncGen) {
console.log(num); // 0, 1, 2,每次间隔 100ms
}
})();
AsyncGenerator 和 Generator 的区别:next() 返回 Promise<IteratorResult<T>> 而非直接返回 IteratorResult<T>。
小结
- 实现
Symbol.iterator的对象是 Iterable,可以被for..of遍历 for..of的循环变量类型自动从 Iterable 推断,不需要手写- 生成器函数
function*返回Generator<T, TReturn, TNext>对象 T是 yield 产生的值类型,TReturn是 return 的最终值类型,TNext是next(x)传入值的类型- 生成器同时实现了 Iterable,直接用
for..of即可 - 异步生成器用
async function*+for await..of,返回AsyncGenerator<T, TReturn, TNext>