diff --git a/src/lang/assertUnreachable.ts b/src/lang/assertUnreachable.ts new file mode 100644 index 0000000..35e5943 --- /dev/null +++ b/src/lang/assertUnreachable.ts @@ -0,0 +1,3 @@ +export default function assertUnreachable(x: never): never { + throw new Error(`Unreachable code [${x}]`); +} diff --git a/src/lang/observables/ReadOnlySubject.ts b/src/lang/observables/ReadOnlySubject.ts new file mode 100644 index 0000000..72242d8 --- /dev/null +++ b/src/lang/observables/ReadOnlySubject.ts @@ -0,0 +1,18 @@ +import Disposable from '../disposables/Disposable'; +import Subject from './Subject'; + +export default class ReadOnlySubject { + private readonly _subject: Subject; + + constructor(subject: Subject) { + this._subject = subject; + } + + get value(): T { + return this._subject.value; + } + + public subscribe(listener: (value: T) => void): Disposable { + return this._subject.subscribe(listener); + } +} diff --git a/src/lang/observables/Subject.ts b/src/lang/observables/Subject.ts new file mode 100644 index 0000000..08f2da4 --- /dev/null +++ b/src/lang/observables/Subject.ts @@ -0,0 +1,39 @@ +import Disposable from '../disposables/Disposable'; + +export default class Subject { + private readonly _listeners: Array<(value: T) => void> = []; + private _value: T; + + constructor(value: T) { + this._value = value; + } + + set value(value: T) { + if (this._value === value) { + return; + } + + this._value = value; + this.notifyListeners(value); + } + + get value(): T { + return this._value; + } + + public subscribe(listener: (value: T) => void): Disposable { + const listenerIndex = this._listeners.push(listener); + + listener(this.value); + + return new Disposable(() => { + this._listeners.splice(listenerIndex, 1); + }); + } + + private notifyListeners(value: T): void { + for (const listener of this._listeners) { + listener(value); + } + } +} diff --git a/src/lang/observables/index.ts b/src/lang/observables/index.ts new file mode 100644 index 0000000..fae5a89 --- /dev/null +++ b/src/lang/observables/index.ts @@ -0,0 +1,4 @@ +import Subject from './Subject'; +import ReadOnlySubject from './ReadOnlySubject'; + +export {Subject, ReadOnlySubject};