Skip to content

Commit 3e79caa

Browse files
author
Krzysztof Borowy
committed
fix: better typing
1 parent 7531bc4 commit 3e79caa

File tree

7 files changed

+259
-105
lines changed

7 files changed

+259
-105
lines changed

packages/core/src/AsyncStorage.ts

Lines changed: 35 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -1,19 +1,26 @@
1+
/**
2+
* Copyright (c) React Native Community.
3+
*
4+
* This source code is licensed under the MIT license found in the
5+
* LICENSE file in the root directory of this source tree.
6+
*
7+
*/
8+
19
import {simpleErrorHandler, simpleLogger, noop} from './defaults';
210
import {
311
FactoryOptions,
412
IStorageBackend,
513
LoggerAction,
6-
StorageModel,
714
StorageOptions,
815
} from '../types';
916

10-
class AsyncStorage<STR extends IStorageBackend, VAL = StorageModel<STR>> {
11-
private readonly _backend: STR;
17+
class AsyncStorage<M, T extends IStorageBackend<M>> {
18+
private readonly _backend: T;
1219
private readonly _config: FactoryOptions;
1320
private readonly log: (action: LoggerAction) => void;
1421
private readonly error: (e: Error | string) => void;
1522

16-
constructor(storageBackend: STR, asOptions: FactoryOptions) {
23+
constructor(storageBackend: T, asOptions: FactoryOptions) {
1724
this._backend = storageBackend;
1825
this._config = asOptions;
1926

@@ -35,13 +42,15 @@ class AsyncStorage<STR extends IStorageBackend, VAL = StorageModel<STR>> {
3542
: simpleErrorHandler;
3643
}
3744
}
38-
39-
async get(key: string, opts: StorageOptions = null): Promise<VAL | null> {
40-
let value = null;
45+
async get<K extends keyof M>(
46+
key: K,
47+
opts: StorageOptions = null,
48+
): Promise<M[K] | null> {
49+
let value = null as M[K] | null;
4150
try {
4251
this.log({
4352
action: 'read-single',
44-
key: key,
53+
key: key as string,
4554
});
4655
value = await this._backend.getSingle(key, opts);
4756
} catch (e) {
@@ -51,15 +60,15 @@ class AsyncStorage<STR extends IStorageBackend, VAL = StorageModel<STR>> {
5160
return value;
5261
}
5362

54-
async set(
55-
key: string,
56-
value: VAL,
63+
async set<K extends keyof M>(
64+
key: K,
65+
value: M[K],
5766
opts: StorageOptions = null,
5867
): Promise<void> {
5968
try {
6069
this.log({
6170
action: 'save-single',
62-
key,
71+
key: key as string,
6372
value,
6473
});
6574
await this._backend.setSingle(key, value, opts);
@@ -68,16 +77,16 @@ class AsyncStorage<STR extends IStorageBackend, VAL = StorageModel<STR>> {
6877
}
6978
}
7079

71-
async getMultiple(
72-
keys: Array<string>,
80+
async getMultiple<K extends keyof M>(
81+
keys: Array<K>,
7382
opts: StorageOptions = null,
74-
): Promise<Array<VAL | null>> {
75-
let values: Array<VAL | null> = [];
83+
): Promise<{[k in K]: M[k] | null}> {
84+
let values = {} as {[k in K]: M[k] | null};
7685

7786
try {
7887
this.log({
7988
action: 'read-many',
80-
key: keys,
89+
key: keys as string[],
8190
});
8291
values = await this._backend.getMany(keys, opts);
8392
} catch (e) {
@@ -87,8 +96,8 @@ class AsyncStorage<STR extends IStorageBackend, VAL = StorageModel<STR>> {
8796
return values;
8897
}
8998

90-
async setMultiple(
91-
keyValues: Array<{[key: string]: VAL}>,
99+
async setMultiple<K extends keyof M>(
100+
keyValues: Array<{[k in K]: M[k]}>,
92101
opts: StorageOptions = null,
93102
): Promise<void> {
94103
try {
@@ -102,11 +111,11 @@ class AsyncStorage<STR extends IStorageBackend, VAL = StorageModel<STR>> {
102111
}
103112
}
104113

105-
async remove(key: string, opts: StorageOptions = null): Promise<void> {
114+
async remove(key: keyof M, opts: StorageOptions = null): Promise<void> {
106115
try {
107116
this.log({
108117
action: 'delete-single',
109-
key,
118+
key: key as string,
110119
});
111120
await this._backend.removeSingle(key, opts);
112121
} catch (e) {
@@ -115,22 +124,22 @@ class AsyncStorage<STR extends IStorageBackend, VAL = StorageModel<STR>> {
115124
}
116125

117126
async removeMultiple(
118-
keys: Array<string>,
127+
keys: Array<keyof M>,
119128
opts: StorageOptions = null,
120129
): Promise<void> {
121130
try {
122131
this.log({
123132
action: 'delete-many',
124-
key: keys,
133+
key: keys as string[],
125134
});
126135
await this._backend.removeMany(keys, opts);
127136
} catch (e) {
128137
this.error(e);
129138
}
130139
}
131140

132-
async getKeys(opts: StorageOptions = null): Promise<Array<string>> {
133-
let keys: Array<string> = [];
141+
async getKeys(opts: StorageOptions = null): Promise<Array<keyof M>> {
142+
let keys: Array<keyof M> = [];
134143

135144
try {
136145
this.log({
@@ -157,7 +166,7 @@ class AsyncStorage<STR extends IStorageBackend, VAL = StorageModel<STR>> {
157166

158167
// todo: think how we could provide additional functions through AS, without returning the instance
159168
// some kind of extension-like functionality
160-
instance(): STR {
169+
instance(): T {
161170
return this._backend;
162171
}
163172
}

packages/core/src/defaults.ts

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,11 @@
1+
/**
2+
* Copyright (c) React Native Community.
3+
*
4+
* This source code is licensed under the MIT license found in the
5+
* LICENSE file in the root directory of this source tree.
6+
*
7+
*/
8+
19
import {FactoryOptions, LoggerAction} from '../types';
210

311
export const factoryOptions: FactoryOptions = {

packages/core/src/index.ts

Lines changed: 13 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,14 @@
1+
/**
2+
* Copyright (c) React Native Community.
3+
*
4+
* This source code is licensed under the MIT license found in the
5+
* LICENSE file in the root directory of this source tree.
6+
*
7+
*/
8+
19
import AsyncStorage from './AsyncStorage';
210
import {factoryOptions} from './defaults';
3-
import {IStorageBackend, FactoryOptions, StorageModel} from '../types';
11+
import {IStorageBackend, FactoryOptions, EmptyStorageModel} from '../types';
412

513
class AsyncStorageFactory {
614
constructor() {
@@ -9,11 +17,11 @@ class AsyncStorageFactory {
917
);
1018
}
1119

12-
static create<STR extends IStorageBackend>(
13-
storage: STR,
20+
static create<M = EmptyStorageModel>(
21+
storage: IStorageBackend,
1422
opts: FactoryOptions = factoryOptions,
15-
): AsyncStorage<STR, StorageModel<STR>> {
16-
return new AsyncStorage(storage, opts);
23+
) {
24+
return new AsyncStorage<M, IStorageBackend<M>>(storage, opts);
1725
}
1826
}
1927

packages/core/types/index.d.ts

Lines changed: 68 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -1,63 +1,94 @@
1-
export class AsyncStorage<
2-
STR extends IStorageBackend,
3-
VAL = StorageModel<STR>
4-
> {
5-
get(key: string, opts?: StorageOptions): Promise<VAL | null>;
1+
/**
2+
* Copyright (c) React Native Community.
3+
*
4+
* This source code is licensed under the MIT license found in the
5+
* LICENSE file in the root directory of this source tree.
6+
*
7+
*/
8+
9+
/**
10+
* Crates an Async Storage instance for given storage backend
11+
* Enhanced by Factory Options, to, in example, enable logging
12+
*/
13+
export default class AsyncStorageFactory {
14+
static create<M = EmptyStorageModel>(
15+
storage: IStorageBackend,
16+
opts: FactoryOptions,
17+
): AsyncStorage<M, IStorageBackend<M>>;
18+
}
19+
20+
/**
21+
* Do not instantiate. Create through AsyncStorageFactory.
22+
* Main API that will be used to call underlying storage backend
23+
*/
24+
export class AsyncStorage<M, T extends IStorageBackend<M>> {
25+
get<K extends keyof M>(key: K, opts?: StorageOptions): Promise<M[K] | null>;
626

7-
set(key: string, value: VAL, opts?: StorageOptions): Promise<void>;
27+
set<K extends keyof M>(
28+
key: K,
29+
value: M[K],
30+
opts?: StorageOptions,
31+
): Promise<void>;
832

9-
getMultiple(
10-
keys: Array<string>,
33+
getMultiple<K extends keyof M>(
34+
keys: Array<K>,
1135
opts?: StorageOptions,
12-
): Promise<Array<VAL | null>>;
36+
): Promise<{[k in K]: M[k] | null}>;
1337

14-
setMultiple(
15-
keyValues: Array<{[key: string]: VAL}>,
38+
setMultiple<K extends keyof M>(
39+
keyValues: Array<Partial<{[k in keyof M]: M[k]}>>,
1640
opts?: StorageOptions,
1741
): Promise<void>;
1842

19-
remove(key: string, opts?: StorageOptions): Promise<void>;
43+
remove(key: keyof M, opts?: StorageOptions): Promise<void>;
2044

21-
removeMultiple(keys: Array<string>, opts?: StorageOptions): Promise<void>;
45+
removeMultiple(keys: Array<keyof M>, opts?: StorageOptions): Promise<void>;
2246

23-
getKeys(opts?: StorageOptions): Promise<Array<string>>;
47+
getKeys(opts?: StorageOptions): Promise<Array<keyof M>>;
2448

2549
clearStorage(opts?: StorageOptions): Promise<void>;
2650

27-
instance(): STR;
51+
instance(): T;
2852
}
2953

30-
export default class AsyncStorageFactory {
31-
static create<STR extends IStorageBackend>(
32-
storage: STR,
33-
opts: FactoryOptions,
34-
): AsyncStorage<STR, StorageModel<STR>>;
35-
}
36-
37-
export interface IStorageBackend<VAL = any> {
38-
getSingle(key: string, opts?: StorageOptions): Promise<VAL | null>;
54+
/**
55+
* Interface that must be implemented by any Storage Backend
56+
* Provides basic API for reading/writing and deleting of data
57+
*/
58+
export interface IStorageBackend<T = EmptyStorageModel> {
59+
getSingle<K extends keyof T>(
60+
key: K,
61+
opts?: StorageOptions,
62+
): Promise<T[K] | null>;
3963

40-
setSingle(key: string, value: VAL, opts?: StorageOptions): Promise<void>;
64+
setSingle<K extends keyof T>(
65+
key: K,
66+
value: T[K],
67+
opts?: StorageOptions,
68+
): Promise<void>;
4169

42-
getMany(
43-
keys: Array<string>,
70+
getMany<K extends keyof T>(
71+
keys: Array<K>,
4472
opts?: StorageOptions,
45-
): Promise<Array<VAL | null>>;
73+
): Promise<{[k in K]: T[k] | null}>;
4674

47-
setMany(
48-
values: Array<{[key: string]: VAL}>,
75+
setMany<K extends keyof T>(
76+
values: Array<{[k in K]: T[k]}>,
4977
opts?: StorageOptions,
5078
): Promise<void>;
5179

52-
removeSingle(key: string, opts?: StorageOptions): Promise<void>;
80+
removeSingle(key: keyof T, opts?: StorageOptions): Promise<void>;
5381

54-
removeMany(keys: Array<string>, opts?: StorageOptions): Promise<void>;
82+
removeMany(keys: Array<keyof T>, opts?: StorageOptions): Promise<void>;
5583

56-
getKeys(opts?: StorageOptions): Promise<Array<string>>;
84+
getKeys(opts?: StorageOptions): Promise<Array<keyof T>>;
5785

5886
dropStorage(opts?: StorageOptions): Promise<void>;
5987
}
6088

89+
/**
90+
* Used by Factory to enhance calls
91+
*/
6192
export type FactoryOptions = {
6293
logger?: ((action: LoggerAction) => void) | boolean;
6394
errorHandler?: ((error: Error | string) => void) | boolean;
@@ -78,8 +109,12 @@ export type LoggerAction = {
78109
key?: string | Array<string>;
79110
};
80111

112+
// Helper types
113+
81114
export type StorageModel<T> = T extends IStorageBackend<infer V> ? V : any;
82115

116+
export type EmptyStorageModel = {[key in symbol | number | string]: any};
117+
83118
export type StorageOptions = {
84119
[key: string]: any;
85120
} | null;

0 commit comments

Comments
 (0)