-
Notifications
You must be signed in to change notification settings - Fork 83
Refactor OnyxUpdate type to bring back type safety #696
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Changes from all commits
70f769d
00d9891
ffb0db8
00c9e40
2e98c2c
cb08498
2979ece
541d23e
0ff8075
d234f8c
0f77bec
0443e3b
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,4 +1,6 @@ | ||
| *.d.ts | ||
| dist | ||
| node_modules | ||
| *.config.js | ||
| *.config.js | ||
| # tests/types catalog is not type checked with the rest of the project, so we need to ignore it in eslint | ||
| tests/types/**/*.ts | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -30,3 +30,5 @@ jobs: | |
| - run: npm run test | ||
| env: | ||
| CI: true | ||
|
|
||
| - run: npm run test:types | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,5 +1,4 @@ | ||
| import type {Merge} from 'type-fest'; | ||
| import type {BuiltIns} from 'type-fest/source/internal'; | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This is not a stable api in type-fest, it was actually moved to another file in next type-fest major version which can lead to a lot of silent type errors in projects using react-native-onyx. |
||
| import type OnyxUtils from './OnyxUtils'; | ||
| import type {OnyxMethod} from './OnyxUtils'; | ||
| import type {FastMergeReplaceNullPatch} from './utils'; | ||
|
|
@@ -157,6 +156,10 @@ type OnyxValue<TKey extends OnyxKey> = string extends TKey ? unknown : TKey exte | |
| /** 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 | ||
|
|
@@ -205,13 +208,7 @@ type NullishObjectDeep<ObjectType extends object> = { | |
| * 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, TMap = never> = { | ||
| [MapK in keyof TMap]: MapK extends `${TKey}${string}` | ||
| ? MapK extends `${TKey}` | ||
| ? never // forbids empty id | ||
| : TValue | ||
| : never; | ||
| }; | ||
|
Comment on lines
-208
to
-214
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This wasn't working as expected, so needed to refactor this a bit @fabioh8010. Which caused a lot of type errors in tests and lib catalogs There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Did some testing on E/App and looks okay I think 👌 |
||
| type Collection<TKey extends CollectionKeyBase, TValue> = Record<`${TKey}${string}`, TValue> & {[P in TKey]?: never}; | ||
|
|
||
| /** 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! | ||
|
|
@@ -322,48 +319,58 @@ 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, TMap = object> = Collection<TKey, NonNullable<OnyxInput<TKey>>, TMap>; | ||
| type OnyxMergeCollectionInput<TKey extends OnyxKey> = Collection<TKey, NonNullable<OnyxInput<TKey>>>; | ||
|
|
||
| type OnyxMethodMap = typeof OnyxUtils.METHOD; | ||
| /** | ||
| * 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>>; | ||
|
|
||
| // Maps onyx methods to their corresponding value types | ||
| type OnyxMethodValueMap = { | ||
| [OnyxUtils.METHOD.SET]: { | ||
| key: OnyxKey; | ||
| value: OnyxSetInput<OnyxKey>; | ||
| }; | ||
| [OnyxUtils.METHOD.MULTI_SET]: { | ||
| key: OnyxKey; | ||
| value: OnyxMultiSetInput; | ||
| }; | ||
| [OnyxUtils.METHOD.MERGE]: { | ||
| key: OnyxKey; | ||
| value: OnyxMergeInput<OnyxKey>; | ||
| }; | ||
| [OnyxUtils.METHOD.CLEAR]: { | ||
| key: OnyxKey; | ||
| value?: undefined; | ||
| }; | ||
| [OnyxUtils.METHOD.MERGE_COLLECTION]: { | ||
| key: CollectionKeyBase; | ||
| value: OnyxMergeCollectionInput<CollectionKeyBase>; | ||
| }; | ||
| [OnyxUtils.METHOD.SET_COLLECTION]: { | ||
| key: CollectionKeyBase; | ||
| value: OnyxMergeCollectionInput<CollectionKeyBase>; | ||
| }; | ||
| }; | ||
| type OnyxMethodMap = typeof OnyxUtils.METHOD; | ||
|
|
||
| /** | ||
| * 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 = { | ||
| [Method in OnyxMethod]: { | ||
| onyxMethod: Method; | ||
| } & OnyxMethodValueMap[Method]; | ||
| }[OnyxMethod]; | ||
| type OnyxUpdate = | ||
| // ⚠️ DO NOT CHANGE THIS TYPE, UNLESS YOU KNOW WHAT YOU ARE DOING. ⚠️ | ||
blazejkustra marked this conversation as resolved.
Show resolved
Hide resolved
|
||
| | { | ||
| [TKey in OnyxKey]: | ||
| | { | ||
| onyxMethod: typeof OnyxUtils.METHOD.SET; | ||
| key: TKey; | ||
| value: OnyxSetInput<TKey>; | ||
| } | ||
| | { | ||
| onyxMethod: typeof OnyxUtils.METHOD.MULTI_SET; | ||
| key: TKey; | ||
| value: OnyxMultiSetInput; | ||
| } | ||
| | { | ||
| onyxMethod: typeof OnyxUtils.METHOD.MERGE; | ||
| key: TKey; | ||
| value: OnyxMergeInput<TKey>; | ||
| } | ||
| | { | ||
| onyxMethod: typeof OnyxUtils.METHOD.CLEAR; | ||
| key: TKey; | ||
| value?: undefined; | ||
| }; | ||
| }[OnyxKey] | ||
| | { | ||
| [TKey in CollectionKeyBase]: | ||
| | { | ||
| onyxMethod: typeof OnyxUtils.METHOD.MERGE_COLLECTION; | ||
| key: TKey; | ||
| value: OnyxMergeCollectionInput<TKey>; | ||
| } | ||
| | { | ||
| onyxMethod: typeof OnyxUtils.METHOD.SET_COLLECTION; | ||
| key: TKey; | ||
| value: OnyxSetCollectionInput<TKey>; | ||
| }; | ||
| }[CollectionKeyBase]; | ||
|
|
||
| /** | ||
| * Represents the options used in `Onyx.set()` method. | ||
|
|
@@ -474,6 +481,7 @@ export type { | |
| OnyxMultiSetInput, | ||
| OnyxMergeInput, | ||
| OnyxMergeCollectionInput, | ||
| OnyxSetCollectionInput, | ||
| OnyxMethod, | ||
| OnyxMethodMap, | ||
| OnyxUpdate, | ||
|
|
||
blazejkustra marked this conversation as resolved.
Show resolved
Hide resolved
|
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,59 @@ | ||
| import type {OnyxUpdate} from '../../dist/types'; | ||
| import ONYX_KEYS from './setup'; | ||
|
|
||
| const onyxUpdate: OnyxUpdate = { | ||
| onyxMethod: 'set', | ||
| key: ONYX_KEYS.TEST_KEY, | ||
| value: 'string', | ||
| }; | ||
|
|
||
| const onyxUpdateError: OnyxUpdate = { | ||
| onyxMethod: 'set', | ||
| key: ONYX_KEYS.TEST_KEY, | ||
| // @ts-expect-error TEST_KEY is a string, not a number | ||
| value: 2, | ||
| }; | ||
|
|
||
| const onyxUpdateCollection: OnyxUpdate = { | ||
| onyxMethod: 'mergecollection', | ||
| key: ONYX_KEYS.COLLECTION.TEST_KEY, | ||
| value: { | ||
| [`${ONYX_KEYS.COLLECTION.TEST_KEY}1`]: { | ||
| str: 'test', | ||
| }, | ||
| [`${ONYX_KEYS.COLLECTION.TEST_KEY}2`]: { | ||
| str: 'test2', | ||
| }, | ||
| }, | ||
| }; | ||
|
|
||
| // @ts-expect-error COLLECTION.TEST_KEY is an object, not a number | ||
| const onyxUpdateCollectionError: OnyxUpdate = { | ||
| onyxMethod: 'mergecollection', | ||
| key: ONYX_KEYS.COLLECTION.TEST_KEY, | ||
| value: { | ||
| [`${ONYX_KEYS.COLLECTION.TEST_KEY}1`]: 2, | ||
| }, | ||
| }; | ||
|
|
||
| const onyxUpdateCollectionError2: OnyxUpdate = { | ||
| onyxMethod: 'mergecollection', | ||
| key: ONYX_KEYS.COLLECTION.TEST_KEY, | ||
| value: { | ||
| [`${ONYX_KEYS.COLLECTION.TEST_KEY}2`]: { | ||
| // @ts-expect-error nonExistingKey is not a valid key | ||
| nonExistingKey: 'test2', | ||
| }, | ||
| }, | ||
| }; | ||
|
|
||
| // @ts-expect-error COLLECTION.TEST_KEY is invalid key, it is missing the suffix | ||
| const onyxUpdateCollectionError3: OnyxUpdate = { | ||
| onyxMethod: 'mergecollection', | ||
| key: ONYX_KEYS.COLLECTION.TEST_KEY, | ||
| value: { | ||
| [ONYX_KEYS.COLLECTION.TEST_KEY]: { | ||
| str: 'test2', | ||
| }, | ||
| }, | ||
| }; |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,26 @@ | ||
| import Onyx from '../../dist/Onyx'; | ||
| import ONYX_KEYS from './setup'; | ||
|
|
||
| Onyx.mergeCollection(ONYX_KEYS.COLLECTION.TEST_KEY, { | ||
| test_1: { | ||
| str: 'test3', | ||
| }, | ||
| test_2: { | ||
| str: 'test4', | ||
| }, | ||
| test_3: { | ||
| str: 'test5', | ||
| }, | ||
| }); | ||
|
|
||
| Onyx.mergeCollection(ONYX_KEYS.COLLECTION.TEST_KEY, { | ||
| // @ts-expect-error COLLECTION.TEST_KEY is invalid key, it is missing the suffix | ||
| test_: { | ||
| str: 'test3', | ||
| }, | ||
| test_2: { | ||
| str: 'test4', | ||
| }, | ||
| // @ts-expect-error COLLECTION.TEST_KEY is object, not a number | ||
| test_3: 2, | ||
| }); |
Uh oh!
There was an error while loading. Please reload this page.