import assertState from "./assertState"; enum NodeType { DummyHead, DummyTail, List, } // We'd prefer to use symbol properties but unfortunately TS doesn't narrow union types when using symbols. type DummyHeadNode = { next: ValueNode | DummyTailNode; type: NodeType.DummyHead; }; type DummyTailNode = { prev: ValueNode | DummyHeadNode; type: NodeType.DummyTail; }; class ValueNode { private detached = false; prev: ValueNode | DummyHeadNode; next: ValueNode | DummyTailNode; type: NodeType.List = NodeType.List; constructor( prev: ValueNode | DummyHeadNode, next: ValueNode | DummyTailNode, private value: T ) { [prev.next, this.prev] = [this, prev]; [next.prev, this.next] = [this, next]; } detach(): void { assertState(!this.detached); this.detached = true; this.prev.next = this.next; this.next.prev = this.prev; } getPrev(): ValueNode | undefined { assertState(!this.detached); const n = this.prev; return n.type == NodeType.List ? n : undefined; } getNext(): ValueNode | undefined { assertState(!this.detached); const n = this.next; return n.type == NodeType.List ? n : undefined; } getValue(): T { return this.value; } setValue(v: T): void { this.value = v; } } export declare type LinkedListNode = Omit< ValueNode, "prev" | "next" | "type" >; export const LinkedListNode = ValueNode; function assertIsValueNode( n: DummyTailNode | ValueNode ): asserts n is ValueNode { assertState( n.type === NodeType.List, `LinkedList node is of type ${n.type} (expected NodeType.List)` ); } export default class LinkedList implements Iterable { // For simplicity, use dummy nodes. private head: DummyHeadNode; private tail: DummyTailNode; constructor() { this.head = { type: NodeType.DummyHead } as any; this.tail = { type: NodeType.DummyTail } as any; this.head.next = this.tail; this.tail.prev = this.head; } *[Symbol.iterator]() { let n = this.head.next; while (n !== this.tail) { assertIsValueNode(n); yield n.getValue(); n = n.next; } } isEmpty() { return !this.getHead(); } append(val: T): ValueNode { const n = new ValueNode(this.tail.prev, this.tail, val); return n; } prepend(val: T): ValueNode { const n = new ValueNode(this.head, this.head.next, val); return n; } getHead(): ValueNode | undefined { const n = this.head.next; return n.type == NodeType.List ? n : undefined; } getTail(): ValueNode | undefined { const n = this.tail.prev; return n.type == NodeType.List ? n : undefined; } }