Skip to content

Commit bf23eb9

Browse files
AdamHerrmannleebyron
authored andcommitted
Allow specific CacheMap key type (#145)
* Allow specific CacheMap key type * Update impl, flow types, and tests
1 parent cd213db commit bf23eb9

File tree

3 files changed

+41
-32
lines changed

3 files changed

+41
-32
lines changed

src/__tests__/dataloader.test.js

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -7,9 +7,12 @@
77
* @flow
88
*/
99

10+
import type { Options } from '..';
1011
const DataLoader = require('..');
1112

12-
function idLoader(options) {
13+
function idLoader<K, C>(
14+
options?: Options<K, K, C>
15+
): [ DataLoader<K, K, C>, Array<$ReadOnlyArray<K>> ] {
1316
const loadCalls = [];
1417
const identityLoader = new DataLoader(keys => {
1518
loadCalls.push(keys);
@@ -568,7 +571,7 @@ describe('Accepts options', () => {
568571
});
569572

570573
describe('Accepts object key in custom cacheKey function', () => {
571-
function cacheKey(key) {
574+
function cacheKey(key: {[string]: any}): string {
572575
return Object.keys(key).sort().map(k => k + ':' + key[k]).join();
573576
}
574577

src/index.d.ts

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -15,9 +15,9 @@
1515
* with different access permissions and consider creating a new instance
1616
* per web request.
1717
*/
18-
declare class DataLoader<K, V> {
18+
declare class DataLoader<K, V, C = K> {
1919

20-
constructor(batchLoadFn: DataLoader.BatchLoadFn<K, V>, options?: DataLoader.Options<K, V>);
20+
constructor(batchLoadFn: DataLoader.BatchLoadFn<K, V>, options?: DataLoader.Options<K, V, C>);
2121

2222
/**
2323
* Loads a key, returning a `Promise` for the value represented by that key.
@@ -43,20 +43,20 @@ declare class DataLoader<K, V> {
4343
* Clears the value at `key` from the cache, if it exists. Returns itself for
4444
* method chaining.
4545
*/
46-
clear(key: K): DataLoader<K, V>;
46+
clear(key: K): this;
4747

4848
/**
4949
* Clears the entire cache. To be used when some event results in unknown
5050
* invalidations across this particular `DataLoader`. Returns itself for
5151
* method chaining.
5252
*/
53-
clearAll(): DataLoader<K, V>;
53+
clearAll(): this;
5454

5555
/**
5656
* Adds the provied key and value to the cache. If the key already exists, no
5757
* change is made. Returns itself for method chaining.
5858
*/
59-
prime(key: K, value: V): DataLoader<K, V>;
59+
prime(key: K, value: V): this;
6060
}
6161

6262
declare namespace DataLoader {
@@ -74,7 +74,7 @@ declare namespace DataLoader {
7474

7575
// Optionally turn off batching or caching or provide a cache key function or a
7676
// custom cache instance.
77-
export type Options<K, V> = {
77+
export type Options<K, V, C = K> = {
7878

7979
/**
8080
* Default `true`. Set to `false` to disable batching,
@@ -102,14 +102,14 @@ declare namespace DataLoader {
102102
* objects are keys and two similarly shaped objects should
103103
* be considered equivalent.
104104
*/
105-
cacheKeyFn?: (key: any) => any,
105+
cacheKeyFn?: (key: K) => C,
106106

107107
/**
108108
* An instance of Map (or an object with a similar API) to
109109
* be used as the underlying cache for this loader.
110110
* Default `new Map()`.
111111
*/
112-
cacheMap?: CacheMap<K, Promise<V>>;
112+
cacheMap?: CacheMap<C, Promise<V>>;
113113
}
114114
}
115115

src/index.js

Lines changed: 28 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -14,12 +14,12 @@ export type BatchLoadFn<K, V> =
1414

1515
// Optionally turn off batching or caching or provide a cache key function or a
1616
// custom cache instance.
17-
export type Options<K, V> = {
17+
export type Options<K, V, C = K> = {
1818
batch?: boolean;
1919
maxBatchSize?: number;
2020
cache?: boolean;
21-
cacheKeyFn?: (key: any) => any;
22-
cacheMap?: CacheMap<K, Promise<V>>;
21+
cacheKeyFn?: (key: K) => C;
22+
cacheMap?: CacheMap<C, Promise<V>>;
2323
};
2424

2525
// If a custom cache is provided, it must be of this type (a subset of ES6 Map).
@@ -40,10 +40,10 @@ export type CacheMap<K, V> = {
4040
* different access permissions and consider creating a new instance per
4141
* web request.
4242
*/
43-
class DataLoader<K, V> {
43+
class DataLoader<K, V, C = K> {
4444
constructor(
4545
batchLoadFn: BatchLoadFn<K, V>,
46-
options?: Options<K, V>
46+
options?: Options<K, V, C>
4747
) {
4848
if (typeof batchLoadFn !== 'function') {
4949
throw new TypeError(
@@ -59,8 +59,8 @@ class DataLoader<K, V> {
5959

6060
// Private
6161
_batchLoadFn: BatchLoadFn<K, V>;
62-
_options: ?Options<K, V>;
63-
_promiseCache: CacheMap<K, Promise<V>>;
62+
_options: ?Options<K, V, C>;
63+
_promiseCache: CacheMap<C, Promise<V>>;
6464
_queue: LoaderQueue<K, V>;
6565

6666
/**
@@ -78,8 +78,7 @@ class DataLoader<K, V> {
7878
var options = this._options;
7979
var shouldBatch = !options || options.batch !== false;
8080
var shouldCache = !options || options.cache !== false;
81-
var cacheKeyFn = options && options.cacheKeyFn;
82-
var cacheKey = cacheKeyFn ? cacheKeyFn(key) : key;
81+
var cacheKey = getCacheKey(options, key);
8382

8483
// If caching and there is a cache-hit, return cached Promise.
8584
if (shouldCache) {
@@ -143,9 +142,8 @@ class DataLoader<K, V> {
143142
* Clears the value at `key` from the cache, if it exists. Returns itself for
144143
* method chaining.
145144
*/
146-
clear(key: K): DataLoader<K, V> {
147-
var cacheKeyFn = this._options && this._options.cacheKeyFn;
148-
var cacheKey = cacheKeyFn ? cacheKeyFn(key) : key;
145+
clear(key: K): this {
146+
var cacheKey = getCacheKey(this._options, key);
149147
this._promiseCache.delete(cacheKey);
150148
return this;
151149
}
@@ -155,7 +153,7 @@ class DataLoader<K, V> {
155153
* invalidations across this particular `DataLoader`. Returns itself for
156154
* method chaining.
157155
*/
158-
clearAll(): DataLoader<K, V> {
156+
clearAll(): this {
159157
this._promiseCache.clear();
160158
return this;
161159
}
@@ -164,9 +162,8 @@ class DataLoader<K, V> {
164162
* Adds the provided key and value to the cache. If the key already
165163
* exists, no change is made. Returns itself for method chaining.
166164
*/
167-
prime(key: K, value: V): DataLoader<K, V> {
168-
var cacheKeyFn = this._options && this._options.cacheKeyFn;
169-
var cacheKey = cacheKeyFn ? cacheKeyFn(key) : key;
165+
prime(key: K, value: V): this {
166+
var cacheKey = getCacheKey(this._options, key);
170167

171168
// Only add the key if it does not already exist.
172169
if (this._promiseCache.get(cacheKey) === undefined) {
@@ -224,7 +221,7 @@ var resolvedPromise;
224221

225222
// Private: given the current state of a Loader instance, perform a batch load
226223
// from its current queue.
227-
function dispatchQueue<K, V>(loader: DataLoader<K, V>) {
224+
function dispatchQueue<K, V>(loader: DataLoader<K, V, any>) {
228225
// Take the current loader queue, replacing it with an empty queue.
229226
var queue = loader._queue;
230227
loader._queue = [];
@@ -245,7 +242,7 @@ function dispatchQueue<K, V>(loader: DataLoader<K, V>) {
245242
}
246243

247244
function dispatchQueueBatch<K, V>(
248-
loader: DataLoader<K, V>,
245+
loader: DataLoader<K, V, any>,
249246
queue: LoaderQueue<K, V>
250247
) {
251248
// Collect all keys to be loaded in this dispatch
@@ -303,7 +300,7 @@ function dispatchQueueBatch<K, V>(
303300
// Private: do not cache individual loads if the entire batch dispatch fails,
304301
// but still reject each request so they do not hang.
305302
function failedDispatch<K, V>(
306-
loader: DataLoader<K, V>,
303+
loader: DataLoader<K, V, any>,
307304
queue: LoaderQueue<K, V>,
308305
error: Error
309306
) {
@@ -313,10 +310,19 @@ function failedDispatch<K, V>(
313310
});
314311
}
315312

313+
// Private: produce a cache key for a given key (and options)
314+
function getCacheKey<K, V, C>(
315+
options: ?Options<K, V, C>,
316+
key: K
317+
): C {
318+
var cacheKeyFn = options && options.cacheKeyFn;
319+
return cacheKeyFn ? cacheKeyFn(key) : (key: any);
320+
}
321+
316322
// Private: given the DataLoader's options, produce a CacheMap to be used.
317-
function getValidCacheMap<K, V>(
318-
options: ?Options<K, V>
319-
): CacheMap<K, Promise<V>> {
323+
function getValidCacheMap<K, V, C>(
324+
options: ?Options<K, V, C>
325+
): CacheMap<C, Promise<V>> {
320326
var cacheMap = options && options.cacheMap;
321327
if (!cacheMap) {
322328
return new Map();

0 commit comments

Comments
 (0)