export interface List extends Iterable { item: T | null; next: List | null; toArray(): Array; } export function atEnd(xs: List): xs is (List & { item: null, next: null }) { return xs.item === null; } export function notAtEnd(xs: List): xs is (List & { item: T, next: List }) { return xs.item !== null; } export class ArrayList implements List { readonly items: Array; readonly index: number = 0; constructor(items: Array, index = 0) { this.items = items; this.index = index; } get item(): T | null { return this.items[this.index] ?? null; } get next(): List | null { if (this.index >= this.items.length) return null; return new ArrayList(this.items, this.index + 1); } toArray(): Array { return this.items.slice(this.index); } [Symbol.iterator](): Iterator { let i: List = this; return { next(): IteratorResult { if (notAtEnd(i)) { const value = i.item; i = i.next; return { done: false, value }; } else { return { done: true, value: null }; } } }; } }