Add observable pattern implementation with Subject and ReadOnlySubject classes

- Introduced Subject class for managing state and notifying listeners
- Added ReadOnlySubject class to provide read-only access to Subject values
- Created assertUnreachable utility function for unreachable code handling
- Updated observables index file to export Subject and ReadOnlySubject
This commit is contained in:
2025-10-30 03:14:53 -04:00
parent 3fe157541c
commit cb34256276
4 changed files with 64 additions and 0 deletions

View File

@@ -0,0 +1,3 @@
export default function assertUnreachable(x: never): never {
throw new Error(`Unreachable code [${x}]`);
}

View File

@@ -0,0 +1,18 @@
import Disposable from '../disposables/Disposable';
import Subject from './Subject';
export default class ReadOnlySubject<T> {
private readonly _subject: Subject<T>;
constructor(subject: Subject<T>) {
this._subject = subject;
}
get value(): T {
return this._subject.value;
}
public subscribe(listener: (value: T) => void): Disposable {
return this._subject.subscribe(listener);
}
}

View File

@@ -0,0 +1,39 @@
import Disposable from '../disposables/Disposable';
export default class Subject<T> {
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);
}
}
}

View File

@@ -0,0 +1,4 @@
import Subject from './Subject';
import ReadOnlySubject from './ReadOnlySubject';
export {Subject, ReadOnlySubject};