Генераторы и итераторы в TypeScript
Дальше: Асинхронность · Дженерики · Циклы
Генераторы (function*) возвращают ленивую последовательность через yield. TypeScript типизирует их как Generator<T> и AsyncGenerator<T>. Полезно для потоков данных, пагинации и обхода без загрузки всего массива в память. Удобный порядок — циклы, асинхронность, эта статья.
Runtime и протокол итерации — итераторы в JavaScript, циклы в TypeScript.
Generator<T>
function* range(from: number, to: number): Generator<number> {
for (let i = from; i <= to; i += 1) {
yield i;
}
}
for (const n of range(1, 3)) {
console.log(n);
}
Разбор:
function*возвращает итерируемый объект с методомnext().- Тип yield —
number; return type генератора —Generator<number, void, unknown>(третий параметр — типyield*).
Явный тип возврата
function* ids(): Generator<string, void, undefined> {
let n = 0;
while (true) {
yield `id-${n += 1}`;
}
}
Iterable и Symbol.iterator
class Counter implements Iterable<number> {
constructor(private readonly max: number) {}
*[Symbol.iterator](): Generator<number> {
for (let i = 1; i <= this.max; i += 1) {
yield i;
}
}
}
for (const n of new Counter(3)) {
console.log(n);
}
Разбор:
for...ofвызываетSymbol.iterator.- Можно реализовать итератор вручную (
next(): IteratorResult<T>), генератор проще.
Делегирование — yield*
function* concat(
a: Iterable<number>,
b: Iterable<number>,
): Generator<number> {
yield* a;
yield* b;
}
Разбор:
yield*перебирает другую последовательность.
AsyncGenerator и for await...of
Код ITЗагрузка примера кода…
Разбор:
- Генератор отдаёт по одному
User, хотя API отвечает страницами — память не забивается всем списком сразу. AsyncGenerator<User>— каждыйyieldпослеawaitвнутриasync function*.- Для production JSON лучше
unknown+ guard — Асинхронное программирование в TypeScript, Рекомендации по разработке на TypeScript; здесьas Page<User>для краткости. - Тот же цикл в Циклы в TypeScript — связка
for await...ofс постраничным API.
Типизация next и ранний выход
function* gen(): Generator<number> {
yield 1;
yield 2;
return 99;
}
const g = gen();
console.log(g.next()); // { value: 1, done: false }
console.log(g.return(0)); // завершение
Разбор:
returnв генераторе даёт{ done: true, value: 99 }.- В прикладном коде чаще используют
breakвfor...of.
Когда использовать
| Сценарий | Подход |
|---|---|
| Ленивый обход большого списка | function* |
| Пагинация API | async function* |
| Простой массив | for...of / map — Коллекции и массивы в TypeScript |
| RxJS-потоки | Observable (вне TS core) |
Частые ошибки
| Ошибка | Что делать |
|---|---|
Забыли * в function* | синтаксическая ошибка |
Путаница Generator и Iterable | реализуйте Symbol.iterator |
Бесконечный генератор без break | условие выхода |
for...of на async gen | только for await...of |
Практика
- Напишите
range(from, to): Generator<number>. - Реализуйте
CounterкакIterable. - Сделайте
async function*с тремя страницами mock API. - Сравните память:
[...range(1, 1_000_000)]и цикл сbreakна 100. - Типизируйте возврат генератора явно.