Class Extends

Node streams as classes: `class X extends Readable` with a `_read` override calling `this.push`, and Writable, Duplex and Transform subclasses overriding `_write` / `_transform` — Node's own subclassing model.

import { Readable, Writable, Duplex, Transform } from 'stream';
import type { TransformCallback } from 'stream';

// A pull-driven Readable subclass: the stream calls _read() when it wants
// more data; object mode keeps each pushed string a chunk of its own.
class Greeter extends Readable {
  words: string[] = ["kalimera", "kosme", "apo", "thessaloniki"];
  i: number = 0;
  constructor() {
    super({ objectMode: true, highWaterMark: 8 });
  }
  _read() {
    if (this.i >= this.words.length) {
      this.push(null);
    } else {
      this.push(this.words[this.i]);
      this.i = this.i + 1;
    }
  }
}

const seen: string[] = [];
const greeter = new Greeter();
greeter.on("data", (w) => { seen.push(w); });
greeter.once("end", () => { console.log("read:", seen.join(" ")); });

// A Writable subclass: each written string arrives as a Buffer.
class Collector extends Writable {
  items: string[] = [];
  _write(chunk: Buffer, _enc: BufferEncoding, cb: (error?: Error | null) => void) {
    this.items.push(chunk.toString());
    cb();
  }
}

const sink = new Collector();
sink.on("finish", () => { console.log("collected:", sink.items.join(",")); });
sink.write("alpha");
sink.write("beta");
sink.end();

// A Duplex subclass: two independent sides on one instance.
class Echo extends Duplex {
  queue: string[] = ["one", "two", "three"];
  i: number = 0;
  received: string[] = [];
  _read() {
    if (this.i >= this.queue.length) {
      this.push(null);
    } else {
      this.push(this.queue[this.i]);
      this.i = this.i + 1;
    }
  }
  _write(chunk: Buffer, _enc: BufferEncoding, cb: (error?: Error | null) => void) {
    this.received.push(chunk.toString());
    cb();
  }
}

const echo = new Echo();
const echoed: string[] = [];
echo.on("data", (c) => { echoed.push(c.toString()); });
echo.once("end", () => { console.log("echo read:", echoed.join(" ")); });
echo.on("finish", () => { console.log("echo wrote:", echo.received.join(",")); });
echo.write("alpha");
echo.write("beta");
echo.end();

// A Transform subclass: _transform rewrites each chunk onto the readable side.
class Upper extends Transform {
  constructor() { super({ objectMode: true }); }
  _transform(chunk: string, _enc: BufferEncoding, cb: TransformCallback) {
    this.push(chunk.toUpperCase());
    cb();
  }
}

const up = new Upper();
const upped: string[] = [];
up.on("data", (c) => { upped.push(c); });
up.once("end", () => { console.log("upper:", upped.join(" ")); });
up.write("kalimera");
up.write("thessaloniki");
up.end();
View source on GitHubexamples/streams/class_extends.ts