Skip to content

Instantly share code, notes, and snippets.

@doronguttman
Last active September 8, 2022 13:33
Show Gist options
  • Save doronguttman/a8a98632066e3dea4af73c16aa60754c to your computer and use it in GitHub Desktop.
Save doronguttman/a8a98632066e3dea4af73c16aa60754c to your computer and use it in GitHub Desktop.
Observable map/set (TypeScript)
import { Observer } from "./vue-helper";
class MapObserver extends Observer {
constructor(value: unknown, shallow?: boolean, mock?: boolean) {
super(value, shallow, mock);
if (!shallow && value instanceof Map) {
this.observeArray(value);
}
}
public override observeArray(value: unknown): void {
// does not support nested Maps
super.observeArray(value instanceof Map ? Array.from(value.values()) : value);
}
}
export class ObservableMap<K, V> extends Map<K, V> {
_length = super.size;
_notify(method: string) {
this._length = super.size;
this.__ob__?.dep?.notify({
type: "map mutation",
target: this,
key: method
});
}
private readonly __ob__: MapObserver;
public constructor(entries?: readonly (readonly [K, V])[] | null) {
super(entries);
this.__ob__ ??= new MapObserver(this);
}
public get length() {
return this._length;
}
public override get size() {
return this._length;
}
public override clear() {
super.clear();
this._notify("clear");
}
public override delete(key: K) {
const deleted = super.delete(key);
this._notify("delete");
return deleted;
}
public override set(key: K, value: V) {
super.set(key, value);
Array.isArray(value) && this.__ob__?.observeArray(value);
this._notify("set");
return this;
}
}
import { Observer } from "./vue-helper";
class SetObserver extends Observer {
constructor(value: unknown, shallow?: boolean, mock?: boolean) {
super(value, shallow, mock);
if (!shallow && value instanceof Set) {
this.observeArray(value);
}
}
public override observeArray(value: unknown): void {
// does not support nested Sets/Maps
super.observeArray(value instanceof Set ? Array.from(value.values()) : value);
}
}
export class ObservableSet<T> extends Set<T> {
_length = super.size;
_notify(method: string) {
this._length = super.size;
this.__ob__?.dep?.notify({
type: "map mutation",
target: this,
key: method
});
}
private readonly __ob__: SetObserver;
public constructor(values?: readonly T[] | null | undefined);
public constructor(iterable?: Iterable<T> | null | undefined) {
super(iterable);
this.__ob__ ??= new SetObserver(this);
}
public get length() {
return this._length;
}
public override get size() {
return this._length;
}
public override clear() {
super.clear();
this._notify("clear");
}
public override add(value: T): this {
super.add(value);
this._notify("add");
return this;
}
public override delete(value: T): boolean {
const result = super.delete(value);
this._notify("delete");
return result;
}
}
import Vue from "vue";
interface IDep {
notify(options: {
type: string;
target: unknown;
key: string;
}): void;
}
interface IObserver {
new(value: unknown, shallow?: boolean, mock?: boolean): IObserver;
observeArray(value: unknown): void;
dep: IDep;
}
export const Observer = Object.getPrototypeOf((Vue.observable({}) as any)["__ob__"]).constructor as new (value: unknown, shallow?: boolean, mock?: boolean) => IObserver;
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment