Skip to content

Instantly share code, notes, and snippets.

@intrnl
Last active September 3, 2021 14:56
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save intrnl/a7cc9a0864e130d9740e9b7baa2c3c08 to your computer and use it in GitHub Desktop.
Save intrnl/a7cc9a0864e130d9740e9b7baa2c3c08 to your computer and use it in GitHub Desktop.
Tree-shakeable class methods
import { Module } from './module';
export class Foo extends Module {
_foo = 'foo';
}
export class Bar extends Module {
_bar = 'bar';
}
export function fooOnly (foo: Foo, str: string) {
return foo._foo;
}
export function barOnly (bar: Bar, num: number) {
return bar._bar;
}
let foo = new Foo();
foo.do(fooOnly, ''); // should pass
foo.do(barOnly, 0); // should fail
let bar = new Bar();
bar.do(fooOnly, ''); // should fail
bar.do(barOnly, 0); // should pass
export type Extension<T> = (mod: T, ...args: any[]) => any;
export type ExtensionParameters<T extends (mod: any, ...args: any[]) => any> =
T extends (mod: any, ...args: infer P) => any ? P : never;
export abstract class Module {
do<T extends Extension<this>> (fn: T, ...args: ExtensionParameters<T>): ReturnType<T> {
return fn(this, ...args);
}
}
@intrnl
Copy link
Author

intrnl commented Aug 12, 2021

Based on this HTTP 203 video, wanting to see if TypeScript authoring is possible.

Class methods has the instance on the first parameter, instead of relying on this, which makes it possible to author them in arrow functions, or even easily opt out from using the do method entirely.

let store = new Store();
store.do(get, 'key');
get(store, 'key');

@intrnl
Copy link
Author

intrnl commented Sep 3, 2021

Treeshake-able IndexedDB key value store

export class IDBKeyval extends Module {
	_idb: Promise<IDBDatabase>;
	_name: string;

	constructor (db = 'keyval-store', store = 'store') {
		super();
	
		this._name = store;
		this._idb = new Promise((resolve, reject) => {
			const request = indexedDB.open(db);
			
			request.onupgradeneeded = () => request.result.createObjectStore(store);
			request.onerror = () => reject(request.error);
			request.onsuccess = () => resolve(request.result);
		});
	}
}

function perform<T> (store: IDBKeyval, mode: IDBTransactionMode, callback: (store: IDBObjectStore) => T | PromiseLike<T>): Promise<void> {
	return store._idb.then((db) => new Promise((resolve, reject) => {
		const transaction = db.transaction(store._name, mode);
		transaction.oncomplete = () => resolve();
		transaction.onerror = transaction.onabort = () => reject(transaction.error);

		callback(transaction.objectStore(store._name));
	}))
}

export function get<T> (store: IDBKeyval, key: IDBValidKey): Promise<T> {
	let request: IDBRequest<T>;
	
	return perform(store, 'readonly', (store) => request = store.get(key))
		.then(() => request.result);
}

export function keys (store: IDBKeyval): Promise<IDBValidKey[]> {
	let request: IDBRequest<IDBValidKey[]>;

	return perform(store, 'readonly', (store) => request = store.getAllKeys())
		.then(() => request.result)
}

export function set<T> (store: IDBKeyval, key: IDBValidKey, value: T): Promise<void> {
	return perform(store, 'readwrite', (store) => store.put(value, key));
}

export function remove (store: IDBKeyval, key: IDBValidKey): Promise<void> {
	return perform(store, 'readwrite', (store) => store.delete(key));
}

export function clear (store: IDBKeyval): Promise<void> {
	return perform(store, 'readwrite', (store) => store.clear());
}

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment