Skip to content

Commit 00c9e40

Browse files
committed
Fix type safety, second try
1 parent ffb0db8 commit 00c9e40

5 files changed

Lines changed: 66 additions & 27 deletions

File tree

lib/Onyx.ts

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -374,7 +374,7 @@ function merge<TKey extends OnyxKey>(key: TKey, changes: OnyxMergeInput<TKey>):
374374
* @param collectionKey e.g. `ONYXKEYS.COLLECTION.REPORT`
375375
* @param collection Object collection keyed by individual collection member keys and values
376376
*/
377-
function mergeCollection<TKey extends CollectionKeyBase, TMap>(collectionKey: TKey, collection: OnyxMergeCollectionInput<TKey, TMap>): Promise<void> {
377+
function mergeCollection<TKey extends CollectionKeyBase>(collectionKey: TKey, collection: OnyxMergeCollectionInput<TKey>): Promise<void> {
378378
return OnyxUtils.mergeCollectionWithPatches(collectionKey, collection, undefined, true);
379379
}
380380

@@ -545,7 +545,7 @@ function update(data: OnyxUpdate[]): Promise<void> {
545545
[OnyxUtils.METHOD.SET]: enqueueSetOperation,
546546
[OnyxUtils.METHOD.MERGE]: enqueueMergeOperation,
547547
[OnyxUtils.METHOD.MERGE_COLLECTION]: () => {
548-
const collection = value as Collection<CollectionKey, unknown, unknown>;
548+
const collection = value as Collection<CollectionKey, unknown>;
549549
if (!OnyxUtils.isValidNonEmptyCollectionForMerge(collection)) {
550550
Logger.logInfo('mergeCollection enqueued within update() with invalid or empty value. Skipping this operation.');
551551
return;
@@ -558,7 +558,7 @@ function update(data: OnyxUpdate[]): Promise<void> {
558558
collectionKeys.forEach((collectionKey) => enqueueMergeOperation(collectionKey, mergedCollection[collectionKey]));
559559
}
560560
},
561-
[OnyxUtils.METHOD.SET_COLLECTION]: (k, v) => promises.push(() => setCollection(k, v as Collection<CollectionKey, unknown, unknown>)),
561+
[OnyxUtils.METHOD.SET_COLLECTION]: (k, v) => promises.push(() => setCollection(k, v as Collection<CollectionKey, unknown>)),
562562
[OnyxUtils.METHOD.MULTI_SET]: (k, v) => Object.entries(v as Partial<OnyxInputKeyValueMapping>).forEach(([entryKey, entryValue]) => enqueueSetOperation(entryKey, entryValue)),
563563
[OnyxUtils.METHOD.CLEAR]: () => {
564564
clearPromise = clear();
@@ -611,14 +611,14 @@ function update(data: OnyxUpdate[]): Promise<void> {
611611
promises.push(() =>
612612
OnyxUtils.mergeCollectionWithPatches(
613613
collectionKey,
614-
batchedCollectionUpdates.merge as Collection<CollectionKey, unknown, unknown>,
614+
batchedCollectionUpdates.merge as Collection<CollectionKey, unknown>,
615615
batchedCollectionUpdates.mergeReplaceNullPatches,
616616
true,
617617
),
618618
);
619619
}
620620
if (!utils.isEmptyObject(batchedCollectionUpdates.set)) {
621-
promises.push(() => OnyxUtils.partialSetCollection(collectionKey, batchedCollectionUpdates.set as Collection<CollectionKey, unknown, unknown>));
621+
promises.push(() => OnyxUtils.partialSetCollection(collectionKey, batchedCollectionUpdates.set as Collection<CollectionKey, unknown>));
622622
}
623623
});
624624

@@ -655,7 +655,7 @@ function update(data: OnyxUpdate[]): Promise<void> {
655655
* @param collectionKey e.g. `ONYXKEYS.COLLECTION.REPORT`
656656
* @param collection Object collection keyed by individual collection member keys and values
657657
*/
658-
function setCollection<TKey extends CollectionKeyBase, TMap>(collectionKey: TKey, collection: OnyxMergeCollectionInput<TKey, TMap>): Promise<void> {
658+
function setCollection<TKey extends CollectionKeyBase>(collectionKey: TKey, collection: OnyxMergeCollectionInput<TKey>): Promise<void> {
659659
let resultCollection: OnyxInputKeyValueMapping = collection;
660660
let resultCollectionKeys = Object.keys(resultCollection);
661661

lib/OnyxUtils.ts

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1035,7 +1035,7 @@ function initializeWithDefaultKeyStates(): Promise<void> {
10351035
/**
10361036
* Validate the collection is not empty and has a correct type before applying mergeCollection()
10371037
*/
1038-
function isValidNonEmptyCollectionForMerge<TKey extends CollectionKeyBase, TMap>(collection: OnyxMergeCollectionInput<TKey, TMap>): boolean {
1038+
function isValidNonEmptyCollectionForMerge<TKey extends CollectionKeyBase>(collection: OnyxMergeCollectionInput<TKey>): boolean {
10391039
return typeof collection === 'object' && !Array.isArray(collection) && !utils.isEmptyObject(collection);
10401040
}
10411041

@@ -1241,9 +1241,9 @@ function updateSnapshots(data: OnyxUpdate[], mergeFn: typeof Onyx.merge): Array<
12411241
* @param mergeReplaceNullPatches Record where the key is a collection member key and the value is a list of
12421242
* tuples that we'll use to replace the nested objects of that collection member record with something else.
12431243
*/
1244-
function mergeCollectionWithPatches<TKey extends CollectionKeyBase, TMap>(
1244+
function mergeCollectionWithPatches<TKey extends CollectionKeyBase>(
12451245
collectionKey: TKey,
1246-
collection: OnyxMergeCollectionInput<TKey, TMap>,
1246+
collection: OnyxMergeCollectionInput<TKey>,
12471247
mergeReplaceNullPatches?: MultiMergeReplaceNullPatches,
12481248
isProcessingCollectionUpdate = false,
12491249
): Promise<void> {
@@ -1366,7 +1366,7 @@ function mergeCollectionWithPatches<TKey extends CollectionKeyBase, TMap>(
13661366
* @param collectionKey e.g. `ONYXKEYS.COLLECTION.REPORT`
13671367
* @param collection Object collection keyed by individual collection member keys and values
13681368
*/
1369-
function partialSetCollection<TKey extends CollectionKeyBase, TMap>(collectionKey: TKey, collection: OnyxMergeCollectionInput<TKey, TMap>): Promise<void> {
1369+
function partialSetCollection<TKey extends CollectionKeyBase>(collectionKey: TKey, collection: OnyxMergeCollectionInput<TKey>): Promise<void> {
13701370
let resultCollection: OnyxInputKeyValueMapping = collection;
13711371
let resultCollectionKeys = Object.keys(resultCollection);
13721372

lib/types.ts

Lines changed: 6 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,4 @@
11
import type {Merge} from 'type-fest';
2-
import type {BuiltIns} from 'type-fest/source/internal';
32
import type OnyxUtils from './OnyxUtils';
43
import type {OnyxMethod} from './OnyxUtils';
54
import type {FastMergeReplaceNullPatch} from './utils';
@@ -157,6 +156,10 @@ type OnyxValue<TKey extends OnyxKey> = string extends TKey ? unknown : TKey exte
157156
/** Utility type to extract `TOnyxValue` from `OnyxCollection<TOnyxValue>` */
158157
type ExtractOnyxCollectionValue<TOnyxCollection> = TOnyxCollection extends NonNullable<OnyxCollection<infer U>> ? U : never;
159158

159+
type Primitive = null | undefined | string | number | boolean | symbol | bigint;
160+
161+
type BuiltIns = Primitive | void | Date | RegExp;
162+
160163
type NonTransformableTypes =
161164
| BuiltIns
162165
// eslint-disable-next-line @typescript-eslint/no-explicit-any
@@ -205,13 +208,7 @@ type NullishObjectDeep<ObjectType extends object> = {
205208
* Also, the `TMap` type is inferred automatically in `mergeCollection()` method and represents
206209
* the object of collection keys/values specified in the second parameter of the method.
207210
*/
208-
type Collection<TKey extends CollectionKeyBase, TValue, TMap = never> = {
209-
[MapK in keyof TMap]: MapK extends `${TKey}${string}`
210-
? MapK extends `${TKey}`
211-
? never // forbids empty id
212-
: TValue
213-
: never;
214-
};
211+
type Collection<TKey extends CollectionKeyBase, TValue> = Record<`${TKey}${string}`, TValue> & {[P in TKey]?: never};
215212

216213
/** Represents the base options used in `Onyx.connect()` method. */
217214
// NOTE: Any changes to this type like adding or removing options must be accounted in OnyxConnectionManager's `generateConnectionID()` method!
@@ -322,7 +319,7 @@ type OnyxMergeInput<TKey extends OnyxKey> = OnyxInput<TKey>;
322319
/**
323320
* This represents the value that can be passed to `Onyx.merge` and to `Onyx.update` with the method "MERGE"
324321
*/
325-
type OnyxMergeCollectionInput<TKey extends OnyxKey, TMap = object> = Collection<TKey, NonNullable<OnyxInput<TKey>>, TMap>;
322+
type OnyxMergeCollectionInput<TKey extends OnyxKey> = Collection<TKey, NonNullable<OnyxInput<TKey>>>;
326323

327324
type OnyxMethodMap = typeof OnyxUtils.METHOD;
328325

tests/types/test.ts

Lines changed: 49 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,12 @@
1-
import type {OnyxUpdate} from '../../dist/types';
1+
import Onyx from '../../dist/Onyx';
2+
import type {Collection, KeyValueMapping, NullishDeep, OnyxInput, OnyxUpdate} from '../../dist/types';
23

34
const ONYX_KEYS = {
45
TEST_KEY: 'test',
56
COLLECTION: {
67
TEST_KEY: 'test_',
78
},
8-
};
9+
} as const;
910

1011
type OnyxValues = {
1112
[ONYX_KEYS.TEST_KEY]: string;
@@ -19,7 +20,7 @@ declare module '../../dist/types' {
1920
interface CustomTypeOptions {
2021
keys: keyof OnyxValues;
2122
collectionKeys: keyof OnyxCollectionValues;
22-
values: OnyxValues;
23+
values: OnyxValues & OnyxCollectionValues;
2324
}
2425
}
2526

@@ -31,7 +32,7 @@ const onyxUpdate: OnyxUpdate = {
3132

3233
const onyxUpdateError: OnyxUpdate = {
3334
onyxMethod: 'set',
34-
key: 'string',
35+
key: ONYX_KEYS.TEST_KEY,
3536
// @ts-expect-error TEST_KEY is a string, not a number
3637
value: 2,
3738
};
@@ -40,13 +41,54 @@ const onyxUpdateCollection: OnyxUpdate = {
4041
onyxMethod: 'mergecollection',
4142
key: ONYX_KEYS.COLLECTION.TEST_KEY,
4243
value: {
43-
[ONYX_KEYS.COLLECTION.TEST_KEY]: {str: 'test'},
44+
[`${ONYX_KEYS.COLLECTION.TEST_KEY}1`]: {
45+
str: 'test',
46+
},
47+
[`${ONYX_KEYS.COLLECTION.TEST_KEY}2`]: {
48+
str: 'test2',
49+
},
4450
},
4551
};
4652

53+
// @ts-expect-error COLLECTION.TEST_KEY is an object, not a number
4754
const onyxUpdateCollectionError: OnyxUpdate = {
4855
onyxMethod: 'mergecollection',
4956
key: ONYX_KEYS.COLLECTION.TEST_KEY,
50-
// @ts-expect-error TEST_KEY is an object, not a number
51-
value: 2,
57+
value: {
58+
[`${ONYX_KEYS.COLLECTION.TEST_KEY}1`]: 2,
59+
},
60+
};
61+
62+
const onyxUpdateCollectionError2: OnyxUpdate = {
63+
onyxMethod: 'mergecollection',
64+
key: ONYX_KEYS.COLLECTION.TEST_KEY,
65+
value: {
66+
[`${ONYX_KEYS.COLLECTION.TEST_KEY}2`]: {
67+
// @ts-expect-error nonExistingKey is not a valid key
68+
nonExistingKey: 'test2',
69+
},
70+
},
71+
};
72+
73+
// @ts-expect-error COLLECTION.TEST_KEY is invalid key, it is missing the suffix
74+
const onyxUpdateCollectionError3: OnyxUpdate = {
75+
onyxMethod: 'mergecollection',
76+
key: ONYX_KEYS.COLLECTION.TEST_KEY,
77+
value: {
78+
[ONYX_KEYS.COLLECTION.TEST_KEY]: {
79+
str: 'test2',
80+
},
81+
},
5282
};
83+
84+
Onyx.mergeCollection(ONYX_KEYS.COLLECTION.TEST_KEY, {
85+
// @ts-expect-error COLLECTION.TEST_KEY is invalid key, it is missing the suffix
86+
test_: {
87+
str: 'test3',
88+
},
89+
test_2: {
90+
str: 'test4',
91+
},
92+
// @ts-expect-error COLLECTION.TEST_KEY is object, not a number
93+
test_3: 2,
94+
});

tests/utils/GenericCollection.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
import type {Collection} from '../../lib/types';
22

3-
type GenericCollection = Collection<string, unknown, unknown>;
3+
type GenericCollection = Collection<string, unknown>;
44

55
export default GenericCollection;

0 commit comments

Comments
 (0)