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

Walkthrough

Space play step click any line
Three takeaways
  1. 1Generators produce values on demand, so chained operations run element-by-element instead of materializing each stage.
  2. 2Wrapping generators in a class with methods that return new instances gives you a fluent, composable pipeline API.
  3. 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.

Building a lazy iterator pipeline in TypeScript — share card
Made with highlit — turn any snippet into a walkthrough like this in about a minute.
Explain your code