typescript
52 lines · 7 steps
Building a lazy iterator pipeline in TypeScript
Generator functions and a chainable wrapper let you compose map, filter, and take without ever building intermediate arrays.
Explained by
highlit
1function* map<T, U>(source: Iterable<T>, fn: (value: T, index: number) => U): Generator<U> {
2 let index = 0;
3 for (const value of source) {
4 yield fn(value, index++);
5 }
6}
7
8function* filter<T>(source: Iterable<T>, predicate: (value: T, index: number) => boolean): Generator<T> {
9 let index = 0;
10 for (const value of source) {
11 if (predicate(value, index++)) {
12 yield value;
13 }
14 }
15}
16
17function* take<T>(source: Iterable<T>, count: number): Generator<T> {
18 if (count <= 0) return;
19 let taken = 0;
20 for (const value of source) {
21 yield value;
22 if (++taken >= count) return;
23 }
24}
25
26class Lazy<T> implements Iterable<T> {
27 constructor(private readonly source: Iterable<T>) {}
28
29 static from<T>(source: Iterable<T>): Lazy<T> {
30 return new Lazy(source);
31 }
32
33 map<U>(fn: (value: T, index: number) => U): Lazy<U> {
34 return new Lazy(map(this.source, fn));
35 }
36
37 filter(predicate: (value: T, index: number) => boolean): Lazy<T> {
38 return new Lazy(filter(this.source, predicate));
39 }
40
41 take(count: number): Lazy<T> {
42 return new Lazy(take(this.source, count));
43 }
44
45 toArray(): T[] {
46 return [...this.source];
47 }
48
49 [Symbol.iterator](): Iterator<T> {
50 return this.source[Symbol.iterator]();
51 }
52}
01 / 01
STEP 01
‹ swipe to step through ›
Walkthrough
Space play
←→ step
click any line
Three takeaways
- 1Generators produce values on demand, so chained operations run element-by-element instead of materializing each stage.
- 2Wrapping generators in a class with methods that return new instances gives you a fluent, composable pipeline API.
- 3Implementing Symbol.iterator makes a class a first-class iterable that spreads, destructures, and loops like native collections.
Related explainers
typescript
import { Injectable, UnauthorizedException } from '@nestjs/common'; import { PassportStrategy } from '@nestjs/passport'; import { ExtractJwt, Strategy } from 'passport-jwt'; import { ConfigService } from '@nestjs/config';
How a JWT strategy authenticates in NestJS
authentication
jwt
passport
Intermediate
7 steps
typescript
import { Injectable } from '@angular/core'; import { HttpInterceptor, HttpRequest, HttpHandler, HttpEvent, HttpErrorResponse } from '@angular/common/http'; import { BehaviorSubject, Observable, throwError } from 'rxjs'; import { catchError, filter, take, switchMap } from 'rxjs/operators';
Refreshing auth tokens in an Angular interceptor
http-interceptor
token-refresh
rxjs
Advanced
8 steps
typescript
import { CanActivate, ExecutionContext, Injectable, UnauthorizedException } from '@nestjs/common'; import { GqlExecutionContext } from '@nestjs/graphql'; import { Reflector } from '@nestjs/core'; import { JwtService } from '@nestjs/jwt';
How a GraphQL auth guard works in NestJS
authentication
authorization
jwt
Intermediate
7 steps
typescript
type ResizeCallback = (size: { width: number; height: number }) => void; export function observeResize(callback: ResizeCallback, delay = 150) { let timeoutId: ReturnType<typeof setTimeout> | undefined;
Debouncing window resize in TypeScript
debounce
closures
event-listeners
Intermediate
6 steps
typescript
import { Injectable, Logger, OnApplicationShutdown } from '@nestjs/common'; import { InjectRedis } from '@nestjs-modules/ioredis'; import Redis from 'ioredis'; import { DataSource } from 'typeorm';
Graceful shutdown hooks in NestJS
graceful-shutdown
lifecycle-hooks
dependency-injection
Intermediate
7 steps
rust
use std::collections::HashMap; use std::fs::File; use std::io::{self, BufRead, BufReader}; use std::path::Path;
Parsing INI files in Rust
parsing
file-io
hashmap
Intermediate
9 steps
Share this explainer
Here's the card — post it anywhere.
Made with highlit — turn any snippet into a walkthrough like this in about a minute.
Explain your code
Embed this explainer
Drop the interactive walkthrough into a blog or docs. Views never cost a credit.
<iframe src="https://highlit.co/explainers/building-a-lazy-iterator-pipeline-in-typescript-explained-typescript-a503/embed?autoplay=1" width="100%" height="520" loading="lazy" style="border:0"></iframe>
Autoplay is on by default — add ?autoplay=0 to start paused.