From 4e481a51c64084db21d3eea513b13a7a3bd6603a Mon Sep 17 00:00:00 2001 From: Florian Dold Date: Thu, 26 Nov 2020 22:14:46 +0100 Subject: more static typing for transactions (fixes #6653) --- packages/taler-wallet-core/src/util/query.ts | 127 ++++++++++++++++++--------- 1 file changed, 84 insertions(+), 43 deletions(-) (limited to 'packages/taler-wallet-core/src/util') diff --git a/packages/taler-wallet-core/src/util/query.ts b/packages/taler-wallet-core/src/util/query.ts index f533c4cfd..fa0d8beb7 100644 --- a/packages/taler-wallet-core/src/util/query.ts +++ b/packages/taler-wallet-core/src/util/query.ts @@ -59,11 +59,8 @@ export interface StoreParams { /** * Definition of an object store. */ -export class Store { - constructor( - public name: string, - public storeParams?: StoreParams, - ) {} +export class Store { + constructor(public name: N, public storeParams?: StoreParams) {} } /** @@ -273,26 +270,48 @@ class ResultStream { } } -export class TransactionHandle { +type StrKey = string & keyof T; + +type StoreName = S extends Store ? N : never; +type StoreContent = S extends Store ? R : never; +type IndexRecord = Ind extends Index ? R : never; + +export class TransactionHandle> { constructor(private tx: IDBTransaction) {} - put(store: Store, value: T, key?: any): Promise { + put( + store: S, + value: StoreContent, + key?: any, + ): Promise { const req = this.tx.objectStore(store.name).put(value, key); return requestToPromise(req); } - add(store: Store, value: T, key?: any): Promise { + add( + store: S, + value: StoreContent, + key?: any, + ): Promise { const req = this.tx.objectStore(store.name).add(value, key); return requestToPromise(req); } - get(store: Store, key: any): Promise { + get( + store: S, + key: any, + ): Promise | undefined> { const req = this.tx.objectStore(store.name).get(key); return requestToPromise(req); } - getIndexed( - index: Index, + getIndexed< + StoreName extends StrKey, + IndexName extends string, + S extends IDBValidKey, + T + >( + index: Index, key: any, ): Promise { const req = this.tx @@ -302,15 +321,20 @@ export class TransactionHandle { return requestToPromise(req); } - iter(store: Store, key?: any): ResultStream { + iter, T extends StoreTypes[N]>( + store: Store, + key?: any, + ): ResultStream { const req = this.tx.objectStore(store.name).openCursor(key); return new ResultStream(req); } - iterIndexed( - index: Index, - key?: any, - ): ResultStream { + iterIndexed< + StoreName extends StrKey, + IndexName extends string, + S extends IDBValidKey, + T + >(index: Index, key?: any): ResultStream { const req = this.tx .objectStore(index.storeName) .index(index.indexName) @@ -318,13 +342,16 @@ export class TransactionHandle { return new ResultStream(req); } - delete(store: Store, key: any): Promise { + delete, T extends StoreTypes[N]>( + store: Store, + key: any, + ): Promise { const req = this.tx.objectStore(store.name).delete(key); return requestToPromise(req); } - mutate( - store: Store, + mutate, T extends StoreTypes[N]>( + store: Store, key: any, f: (x: T) => T | undefined, ): Promise { @@ -333,10 +360,10 @@ export class TransactionHandle { } } -function runWithTransaction( +function runWithTransaction>( db: IDBDatabase, - stores: Store[], - f: (t: TransactionHandle) => Promise, + stores: StoreTypes[], + f: (t: TransactionHandle) => Promise, mode: "readonly" | "readwrite", ): Promise { const stack = Error("Failed transaction was started here."); @@ -397,7 +424,12 @@ function runWithTransaction( /** * Definition of an index. */ -export class Index { +export class Index< + StoreName extends string, + IndexName extends string, + S extends IDBValidKey, + T +> { /** * Name of the store that this index is associated with. */ @@ -409,8 +441,8 @@ export class Index { options: IndexOptions; constructor( - s: Store, - public indexName: string, + s: Store, + public indexName: IndexName, public keyPath: string | string[], options?: IndexOptions, ) { @@ -539,7 +571,10 @@ export class Database { }); } - async get(store: Store, key: any): Promise { + async get( + store: Store, + key: any, + ): Promise { const tx = this.db.transaction([store.name], "readonly"); const req = tx.objectStore(store.name).get(key); const v = await requestToPromise(req); @@ -547,10 +582,12 @@ export class Database { return v; } - async getIndexed( - index: Index, + async getIndexed>( + index: Ind extends Index + ? Index + : never, key: any, - ): Promise { + ): Promise | undefined> { const tx = this.db.transaction([index.storeName], "readonly"); const req = tx.objectStore(index.storeName).index(index.indexName).get(key); const v = await requestToPromise(req); @@ -558,7 +595,11 @@ export class Database { return v; } - async put(store: Store, value: T, key?: any): Promise { + async put>( + store: St extends Store ? Store : never, + value: St extends Store ? R : never, + key?: any, + ): Promise { const tx = this.db.transaction([store.name], "readwrite"); const req = tx.objectStore(store.name).put(value, key); const v = await requestToPromise(req); @@ -566,8 +607,8 @@ export class Database { return v; } - async mutate( - store: Store, + async mutate( + store: Store, key: any, f: (x: T) => T | undefined, ): Promise { @@ -577,14 +618,14 @@ export class Database { await transactionToPromise(tx); } - iter(store: Store): ResultStream { + iter(store: Store): ResultStream { const tx = this.db.transaction([store.name], "readonly"); const req = tx.objectStore(store.name).openCursor(); return new ResultStream(req); } - iterIndex( - index: Index, + iterIndex( + index: Index, query?: any, ): ResultStream { const tx = this.db.transaction([index.storeName], "readonly"); @@ -595,17 +636,17 @@ export class Database { return new ResultStream(req); } - async runWithReadTransaction( - stores: Store[], - f: (t: TransactionHandle) => Promise, + async runWithReadTransaction>( + stores: StoreTypes[], + f: (t: TransactionHandle) => Promise, ): Promise { - return runWithTransaction(this.db, stores, f, "readonly"); + return runWithTransaction(this.db, stores, f, "readonly"); } - async runWithWriteTransaction( - stores: Store[], - f: (t: TransactionHandle) => Promise, + async runWithWriteTransaction>( + stores: StoreTypes[], + f: (t: TransactionHandle) => Promise, ): Promise { - return runWithTransaction(this.db, stores, f, "readwrite"); + return runWithTransaction(this.db, stores, f, "readwrite"); } } -- cgit v1.2.3