forked from Expensify/react-native-onyx
-
Notifications
You must be signed in to change notification settings - Fork 1
Expand file tree
/
Copy pathtypes.ts
More file actions
502 lines (439 loc) · 18.7 KB
/
types.ts
File metadata and controls
502 lines (439 loc) · 18.7 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
import type {Merge} from 'type-fest';
import type OnyxUtils from './OnyxUtils';
import type {OnyxMethod} from './OnyxUtils';
import type {FastMergeReplaceNullPatch} from './utils';
/**
* Utility type that excludes `null` from the type `TValue`.
*/
type NonNull<TValue> = TValue extends null ? never : TValue;
/**
* Utility type that excludes `undefined` from the type `TValue`.
*/
type NonUndefined<TValue> = TValue extends undefined ? never : TValue;
/**
* Represents a deeply nested record. It maps keys to values,
* and those values can either be of type `TValue` or further nested `DeepRecord` instances.
*/
type DeepRecord<TKey extends string | number | symbol, TValue> = {
[key: string]: TValue | DeepRecord<TKey, TValue>;
};
/**
* Represents type options to configure all Onyx methods.
* It's a combination of predefined options with user-provided options (CustomTypeOptions).
*
* The options are:
* - `keys`: Represents a string union of all Onyx normal keys.
* - `collectionKeys`: Represents a string union of all Onyx collection keys.
* - `values`: Represents a Record where each key is an Onyx key and each value is its corresponding Onyx value type.
*
* The user-defined options (CustomTypeOptions) are merged into these predefined options.
* In case of conflicting properties, the ones from CustomTypeOptions are prioritized.
*/
type TypeOptions = Merge<
{
keys: string;
collectionKeys: string;
values: Record<string, unknown>;
},
CustomTypeOptions
>;
/**
* Represents the user-defined options to configure all Onyx methods.
*
* The developer can configure Onyx methods by augmenting this library and overriding CustomTypeOptions.
*
* @example
* ```ts
* // ONYXKEYS.ts
* import {ValueOf} from 'type-fest';
* import { Account, Report } from './types';
*
* const ONYXKEYS = {
* ACCOUNT: 'account',
* IS_SIDEBAR_LOADED: 'isSidebarLoaded',
*
* // Collection Keys
* COLLECTION: {
* REPORT: 'report_',
* },
* } as const;
*
* type OnyxKeysMap = typeof ONYXKEYS;
* type OnyxCollectionKey = ValueOf<OnyxKeysMap['COLLECTION']>;
* type OnyxKey = DeepValueOf<Omit<OnyxKeysMap, 'COLLECTION'>>;
*
* type OnyxValues = {
* [ONYXKEYS.ACCOUNT]: Account;
* [ONYXKEYS.IS_SIDEBAR_LOADED]: boolean;
* [ONYXKEYS.COLLECTION.REPORT]: Report;
* };
*
* export default ONYXKEYS;
* export type {OnyxKey, OnyxCollectionKey, OnyxValues};
*
* // global.d.ts
* import {OnyxKey, OnyxCollectionKey, OnyxValues} from './ONYXKEYS';
*
* declare module 'react-native-onyx' {
* interface CustomTypeOptions {
* keys: OnyxKey;
* collectionKeys: OnyxCollectionKey;
* values: OnyxValues;
* }
* }
* ```
*/
interface CustomTypeOptions {}
/**
* Represents a string union of all Onyx normal keys.
*/
type Key = TypeOptions['keys'];
/**
* Represents a string union of all Onyx collection keys.
*/
type CollectionKeyBase = TypeOptions['collectionKeys'];
/**
* Represents a literal string union of all Onyx collection keys.
* It allows appending a string after each collection key e.g. `report_some-id`.
*/
type CollectionKey = `${CollectionKeyBase}${string}`;
/**
* Represents a string union of all Onyx normal and collection keys.
*/
type OnyxKey = Key | CollectionKey;
/**
* Represents a selector function type which operates based on the provided `TKey` and `ReturnType`.
*
* A `Selector` is a function that accepts a value and returns a processed value.
* This type accepts two type parameters: `TKey` and `TReturnType`.
*
* The type `TKey` extends `OnyxKey` and it is the key used to access a value in `KeyValueMapping`.
* `TReturnType` is the type of the returned value from the selector function.
*/
type Selector<TKey extends OnyxKey, TReturnType> = (value: OnyxEntry<KeyValueMapping[TKey]>) => TReturnType;
/**
* Represents a single Onyx entry, that can be either `TOnyxValue` or `undefined` if it doesn't exist.
* It can be used to specify data retrieved from Onyx.
*/
type OnyxEntry<TOnyxValue> = TOnyxValue | undefined;
/**
* Represents an Onyx collection of entries, that can be either a record of `TOnyxValue`s or `undefined` if it is empty or doesn't exist.
* It can be used to specify collection data retrieved from Onyx.
*/
type OnyxCollection<TOnyxValue> = OnyxEntry<Record<string, TOnyxValue | undefined>>;
/**
* Represents a mapping of Onyx keys to values, where keys are either normal or collection Onyx keys
* and values are the corresponding values in Onyx's state.
*
* For collection keys, `KeyValueMapping` allows any string to be appended
* to the key (e.g., 'report_some-id', 'download_some-id').
*
* The mapping is derived from the `values` property of the `TypeOptions` type.
*/
type KeyValueMapping = {
[TKey in keyof TypeOptions['values'] as TKey extends CollectionKeyBase ? `${TKey}${string}` : TKey]: TypeOptions['values'][TKey];
};
/**
* Represents a Onyx value that can be either a single entry or a collection of entries, depending on the `TKey` provided.
*/
type OnyxValue<TKey extends OnyxKey> = string extends TKey ? unknown : TKey extends CollectionKeyBase ? OnyxCollection<KeyValueMapping[TKey]> : OnyxEntry<KeyValueMapping[TKey]>;
/** Utility type to extract `TOnyxValue` from `OnyxCollection<TOnyxValue>` */
type ExtractOnyxCollectionValue<TOnyxCollection> = TOnyxCollection extends NonNullable<OnyxCollection<infer U>> ? U : never;
type Primitive = null | undefined | string | number | boolean | symbol | bigint;
type BuiltIns = Primitive | void | Date | RegExp;
type NonTransformableTypes =
| BuiltIns
// eslint-disable-next-line @typescript-eslint/no-explicit-any
| ((...args: any[]) => unknown)
| Map<unknown, unknown>
| Set<unknown>
| ReadonlyMap<unknown, unknown>
| ReadonlySet<unknown>
| unknown[]
| readonly unknown[];
/**
* Create a type from another type with all keys and nested keys set to optional or null.
*
* @example
* const settings: Settings = {
* textEditor: {
* fontSize: 14;
* fontColor: '#000000';
* fontWeight: 400;
* }
* autosave: true;
* };
*
* const applySavedSettings = (savedSettings: NullishDeep<Settings>) => {
* return {...settings, ...savedSettings};
* }
*
* settings = applySavedSettings({textEditor: {fontWeight: 500, fontColor: null}});
*/
type NullishDeep<T> = T extends NonTransformableTypes ? T : T extends object ? NullishObjectDeep<T> : unknown;
/**
* Same as `NullishDeep`, but accepts only `object`s as inputs. Internal helper for `NullishDeep`.
*/
type NullishObjectDeep<ObjectType extends object> = {
[KeyType in keyof ObjectType]?: NullishDeep<ObjectType[KeyType]> | null;
};
/**
* Represents a mapping between Onyx collection keys and their respective values.
*
* It helps to enforce that a Onyx collection key should not be without suffix (e.g. should always be of the form `${TKey}${string}`),
* and to map each Onyx collection key with suffix to a value of type `TValue`.
*
* Also, the `TMap` type is inferred automatically in `mergeCollection()` method and represents
* the object of collection keys/values specified in the second parameter of the method.
*/
type Collection<TKey extends CollectionKeyBase, TValue> = Record<`${TKey}${string}`, TValue>;
/** Represents the base options used in `Onyx.connect()` method. */
// NOTE: Any changes to this type like adding or removing options must be accounted in OnyxConnectionManager's `generateConnectionID()` method!
type BaseConnectOptions = {
/** If set to `false`, then the initial data will be only sent to the callback function if it changes. */
initWithStoredValues?: boolean;
/**
* If set to `false`, the connection won't be reused between other subscribers that are listening to the same Onyx key
* with the same connect configurations.
*/
reuseConnection?: boolean;
};
/** Represents the callback function used in `Onyx.connect()` method with a regular key. */
type DefaultConnectCallback<TKey extends OnyxKey> = (value: OnyxEntry<KeyValueMapping[TKey]>, key: TKey) => void;
/** Represents the callback function used in `Onyx.connect()` method with a collection key. */
type CollectionConnectCallback<TKey extends OnyxKey> = (value: NonUndefined<OnyxCollection<KeyValueMapping[TKey]>>, key: TKey, sourceValue?: OnyxValue<TKey>) => void;
/** Represents the options used in `Onyx.connect()` method with a regular key. */
// NOTE: Any changes to this type like adding or removing options must be accounted in OnyxConnectionManager's `generateConnectionID()` method!
type DefaultConnectOptions<TKey extends OnyxKey> = BaseConnectOptions & {
/** The Onyx key to subscribe to. */
key: TKey;
/** A function that will be called when the Onyx data we are subscribed changes. */
callback?: DefaultConnectCallback<TKey>;
/** If set to `true`, it will return the entire collection to the callback as a single object. */
waitForCollectionCallback?: false;
};
/** Represents the options used in `Onyx.connect()` method with a collection key. */
// NOTE: Any changes to this type like adding or removing options must be accounted in OnyxConnectionManager's `generateConnectionID()` method!
type CollectionConnectOptions<TKey extends OnyxKey> = BaseConnectOptions & {
/** The Onyx key to subscribe to. */
key: TKey extends CollectionKeyBase ? TKey : never;
/** A function that will be called when the Onyx data we are subscribed changes. */
callback?: CollectionConnectCallback<TKey>;
/** If set to `true`, it will return the entire collection to the callback as a single object. */
waitForCollectionCallback: true;
};
/**
* Represents the options used in `Onyx.connect()` method.
* The type is built from `DefaultConnectOptions`/`CollectionConnectOptions` depending on the `waitForCollectionCallback` property.
* It includes two different forms, depending on whether we are waiting for a collection callback or not.
*
* If `waitForCollectionCallback` is `true`, it expects `key` to be a Onyx collection key and `callback` will be triggered with the whole collection
* and will pass `value` as an `OnyxCollection`.
*
* If `waitForCollectionCallback` is `false` or not specified, the `key` can be any Onyx key and `callback` will be triggered with updates of each collection item
* and will pass `value` as an `OnyxEntry`.
*/
// NOTE: Any changes to this type like adding or removing options must be accounted in OnyxConnectionManager's `generateConnectionID()` method!
type ConnectOptions<TKey extends OnyxKey> = DefaultConnectOptions<TKey> | CollectionConnectOptions<TKey>;
type CallbackToStateMapping<TKey extends OnyxKey> = ConnectOptions<TKey> & {
subscriptionID: number;
};
/**
* Represents a single Onyx input value, that can be either `TOnyxValue` or `null` if the key should be deleted.
* This type is used for data passed to Onyx e.g. in `Onyx.merge` and `Onyx.set`.
*/
type OnyxInputValue<TOnyxValue> = TOnyxValue | null;
/**
* Represents an Onyx collection input, that can be either a record of `TOnyxValue`s or `null` if the key should be deleted.
*/
type OnyxCollectionInputValue<TOnyxValue> = OnyxInputValue<Record<string, TOnyxValue | null>>;
/**
* Represents an input value that can be passed to Onyx methods, that can be either `TOnyxValue` or `null`.
* Setting a key to `null` will remove the key from the store.
* `undefined` is not allowed for setting values, because it will have no effect on the data.
*/
type OnyxInput<TKey extends OnyxKey> = OnyxInputValue<NullishDeep<KeyValueMapping[TKey]>>;
/**
* Represents a mapping object where each `OnyxKey` maps to either a value of its corresponding type in `KeyValueMapping` or `null`.
*
* It's very similar to `KeyValueMapping` but this type is used for inputs to Onyx
* (set, merge, mergeCollection) and therefore accepts using `null` to remove a key from Onyx.
*/
type OnyxInputKeyValueMapping = {
[TKey in OnyxKey]: OnyxInput<TKey>;
};
/**
* This represents the value that can be passed to `Onyx.set` and to `Onyx.update` with the method "SET"
*/
type OnyxSetInput<TKey extends OnyxKey> = OnyxInput<TKey>;
/**
* This represents the value that can be passed to `Onyx.multiSet` and to `Onyx.update` with the method "MULTI_SET"
*/
type OnyxMultiSetInput = Partial<OnyxInputKeyValueMapping>;
/**
* This represents the value that can be passed to `Onyx.merge` and to `Onyx.update` with the method "MERGE"
*/
type OnyxMergeInput<TKey extends OnyxKey> = OnyxInput<TKey>;
/**
* This represents the value that can be passed to `Onyx.merge` and to `Onyx.update` with the method "MERGE"
*/
type OnyxMergeCollectionInput<TKey extends OnyxKey> = Collection<TKey, NonNullable<OnyxInput<TKey>>>;
/**
* This represents the value that can be passed to `Onyx.setCollection` and to `Onyx.update` with the method "SET_COLLECTION"
*/
type OnyxSetCollectionInput<TKey extends OnyxKey> = Collection<TKey, OnyxInput<TKey>>;
type OnyxMethodMap = typeof OnyxUtils.METHOD;
type ExpandOnyxKeys<TKey extends OnyxKey> = TKey extends CollectionKeyBase ? NoInfer<`${TKey}${string}`> : TKey;
/**
* OnyxUpdate type includes all onyx methods used in OnyxMethodValueMap.
* If a new method is added to OnyxUtils.METHOD constant, it must be added to OnyxMethodValueMap type.
* Otherwise it will show static type errors.
*/
type OnyxUpdate<TKey extends OnyxKey> = {
// ⚠️ DO NOT CHANGE THIS TYPE, UNLESS YOU KNOW WHAT YOU ARE DOING. ⚠️
[K in TKey]:
| {onyxMethod: typeof OnyxUtils.METHOD.SET; key: ExpandOnyxKeys<K>; value: OnyxSetInput<K>}
| {onyxMethod: typeof OnyxUtils.METHOD.MULTI_SET; key: ExpandOnyxKeys<K>; value: OnyxMultiSetInput}
| {onyxMethod: typeof OnyxUtils.METHOD.MERGE; key: ExpandOnyxKeys<K>; value: OnyxMergeInput<K>}
| {onyxMethod: typeof OnyxUtils.METHOD.CLEAR; key: ExpandOnyxKeys<K>; value?: never}
| {onyxMethod: typeof OnyxUtils.METHOD.MERGE_COLLECTION; key: K; value: OnyxMergeCollectionInput<K>}
| {onyxMethod: typeof OnyxUtils.METHOD.SET_COLLECTION; key: K; value: OnyxSetCollectionInput<K>};
}[TKey];
/**
* Represents the options used in `Onyx.set()` method.
*/
type SetOptions = {
/** Skip the deep equality check against the cached value. Improves performance for large objects. */
skipCacheCheck?: boolean;
};
type SetParams<TKey extends OnyxKey> = {
key: TKey;
value: OnyxSetInput<TKey>;
options?: SetOptions;
};
type SetCollectionParams<TKey extends CollectionKeyBase> = {
collectionKey: TKey;
collection: OnyxSetCollectionInput<TKey>;
};
type MergeCollectionWithPatchesParams<TKey extends CollectionKeyBase> = {
collectionKey: TKey;
collection: OnyxMergeCollectionInput<TKey>;
mergeReplaceNullPatches?: MultiMergeReplaceNullPatches;
isProcessingCollectionUpdate?: boolean;
};
type RetriableOnyxOperation =
| typeof OnyxUtils.setWithRetry
| typeof OnyxUtils.multiSetWithRetry
| typeof OnyxUtils.setCollectionWithRetry
| typeof OnyxUtils.mergeCollectionWithPatches
| typeof OnyxUtils.partialSetCollection;
/**
* Represents the options used in `Onyx.init()` method.
*/
type InitOptions = {
/** `ONYXKEYS` constants object */
keys?: DeepRecord<string, OnyxKey>;
/** initial data to set when `init()` and `clear()` is called */
initialKeyStates?: Partial<OnyxInputKeyValueMapping>;
/**
* This is an array of keys (individual or collection patterns) that when provided to Onyx are flagged
* as "safe" for removal. Any components subscribing to these keys must also implement a canEvict option. See the README for more info.
*/
evictableKeys?: OnyxKey[];
/**
* Sets how many recent keys should we try to keep in cache
* Setting this to 0 would practically mean no cache
* We try to free cache when we connect to a safe eviction key
*/
maxCachedKeysCount?: number;
/**
* Auto synchronize storage events between multiple instances
* of Onyx running in different tabs/windows. Defaults to true for platforms that support local storage (web/desktop)
*/
shouldSyncMultipleInstances?: boolean;
/**
* If enabled, it will connect to Redux DevTools Extension for debugging.
* This allows you to see all Onyx state changes in the Redux DevTools.
* @default true
*/
enableDevTools?: boolean;
/**
* Array of collection member IDs that Onyx should silently ignore across all operations.
* This prevents keys formed from invalid or default IDs (e.g. "-1", "0", "undefined") from
* polluting cache or triggering subscriber notifications.
*/
skippableCollectionMemberIDs?: string[];
/**
* Array of keys that when provided to Onyx are flagged as RAM-only keys, and thus are not saved to disk.
*/
ramOnlyKeys?: OnyxKey[];
/**
* A list of field names that should always be merged into snapshot entries even if those fields are
* missing in the snapshot. Snapshots are saved "views" of a key's data used to populate read-only
* or cached lists, and by default Onyx only merges fields that already exist in that saved view.
* Use this to opt-in to additional fields that must appear in snapshots (for example, pending flags)
* without hardcoding app-specific logic inside Onyx.
*/
snapshotMergeKeys?: string[];
};
// eslint-disable-next-line @typescript-eslint/no-explicit-any
type GenericFunction = (...args: any[]) => any;
/**
* Represents a record where the key is a collection member key and the value is a list of
* tuples that we'll use to replace the nested objects of that collection member record with something else.
*/
type MultiMergeReplaceNullPatches = Record<OnyxKey, FastMergeReplaceNullPatch[]>;
/**
* Represents a combination of Merge and Set operations that should be executed in Onyx
*/
type MixedOperationsQueue = {
merge: OnyxInputKeyValueMapping;
mergeReplaceNullPatches: MultiMergeReplaceNullPatches;
set: OnyxInputKeyValueMapping;
};
export type {
BaseConnectOptions,
Collection,
CollectionConnectCallback,
CollectionConnectOptions,
CollectionKey,
CollectionKeyBase,
ConnectOptions,
CustomTypeOptions,
DeepRecord,
DefaultConnectCallback,
DefaultConnectOptions,
ExtractOnyxCollectionValue,
GenericFunction,
InitOptions,
Key,
KeyValueMapping,
CallbackToStateMapping,
NonNull,
NonUndefined,
OnyxInputKeyValueMapping,
NullishDeep,
OnyxCollection,
OnyxEntry,
OnyxKey,
OnyxInputValue,
OnyxCollectionInputValue,
OnyxInput,
OnyxSetInput,
OnyxMultiSetInput,
OnyxMergeInput,
OnyxMergeCollectionInput,
OnyxSetCollectionInput,
OnyxMethod,
OnyxMethodMap,
OnyxUpdate,
OnyxValue,
Selector,
SetOptions,
SetParams,
SetCollectionParams,
MergeCollectionWithPatchesParams,
MultiMergeReplaceNullPatches,
MixedOperationsQueue,
RetriableOnyxOperation,
};