Skip to content
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

change selector return type #482

Merged
merged 4 commits into from
Mar 1, 2024
Merged
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
16 changes: 8 additions & 8 deletions lib/withOnyx.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -61,9 +61,9 @@ type BaseMappingKey<TComponentProps, TOnyxProps, TOnyxProp extends keyof TOnyxPr
* },
* ```
*/
type BaseMappingStringKeyAndSelector<TComponentProps, TOnyxProps, TOnyxProp extends keyof TOnyxProps, TOnyxKey extends OnyxKey> = {
type BaseMappingStringKeyAndSelector<TComponentProps, TOnyxProps, TReturnType, TOnyxKey extends OnyxKey> = {
key: TOnyxKey;
selector: Selector<TOnyxKey, TOnyxProps, TOnyxProps[TOnyxProp]>;
selector: Selector<TOnyxKey, TOnyxProps, TReturnType>;
};

/**
Expand All @@ -81,9 +81,9 @@ type BaseMappingStringKeyAndSelector<TComponentProps, TOnyxProps, TOnyxProp exte
* },
* ```
*/
type BaseMappingFunctionKeyAndSelector<TComponentProps, TOnyxProps, TOnyxProp extends keyof TOnyxProps, TOnyxKey extends OnyxKey> = {
type BaseMappingFunctionKeyAndSelector<TComponentProps, TOnyxProps, TReturnType, TOnyxKey extends OnyxKey> = {
key: (props: Omit<TComponentProps, keyof TOnyxProps> & Partial<TOnyxProps>) => TOnyxKey;
selector: Selector<TOnyxKey, TOnyxProps, TOnyxProps[TOnyxProp]>;
selector: Selector<TOnyxKey, TOnyxProps, TReturnType>;
};

/**
Expand All @@ -93,8 +93,8 @@ type Mapping<TComponentProps, TOnyxProps, TOnyxProp extends keyof TOnyxProps, TO
EntryBaseMapping<TOnyxKey> &
(
| BaseMappingKey<TComponentProps, TOnyxProps, TOnyxProp, TOnyxKey, OnyxEntry<KeyValueMapping[TOnyxKey]>>
| BaseMappingStringKeyAndSelector<TComponentProps, TOnyxProps, TOnyxProp, TOnyxKey>
| BaseMappingFunctionKeyAndSelector<TComponentProps, TOnyxProps, TOnyxProp, TOnyxKey>
| BaseMappingStringKeyAndSelector<TComponentProps, TOnyxProps, TOnyxProps[TOnyxProp], TOnyxKey>
| BaseMappingFunctionKeyAndSelector<TComponentProps, TOnyxProps, TOnyxProps[TOnyxProp], TOnyxKey>
);

/**
Expand All @@ -104,8 +104,8 @@ type CollectionMapping<TComponentProps, TOnyxProps, TOnyxProp extends keyof TOny
CollectionBaseMapping<TOnyxKey> &
(
| BaseMappingKey<TComponentProps, TOnyxProps, TOnyxProp, TOnyxKey, OnyxCollection<KeyValueMapping[TOnyxKey]>>
| BaseMappingStringKeyAndSelector<TComponentProps, TOnyxProps, TOnyxProp, TOnyxKey>
| BaseMappingFunctionKeyAndSelector<TComponentProps, TOnyxProps, TOnyxProp, TOnyxKey>
| BaseMappingStringKeyAndSelector<TComponentProps, TOnyxProps, Partial<KeyValueMapping[TOnyxKey]>, TOnyxKey>
| BaseMappingFunctionKeyAndSelector<TComponentProps, TOnyxProps, Partial<KeyValueMapping[TOnyxKey]>, TOnyxKey>
Copy link
Contributor

@fabioh8010 fabioh8010 Feb 29, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
| BaseMappingStringKeyAndSelector<TComponentProps, TOnyxProps, Partial<KeyValueMapping[TOnyxKey]>, TOnyxKey>
| BaseMappingFunctionKeyAndSelector<TComponentProps, TOnyxProps, Partial<KeyValueMapping[TOnyxKey]>, TOnyxKey>
| BaseMappingStringKeyAndSelector<TComponentProps, TOnyxProps, ExtractOnyxCollectionValue<TOnyxProps[TOnyxProp]>, TOnyxKey>
| BaseMappingFunctionKeyAndSelector<TComponentProps, TOnyxProps, ExtractOnyxCollectionValue<TOnyxProps[TOnyxProp]>, TOnyxKey>

@tienifr We have two issues here:

  1. By changing TOnyxProps[TOnyxProp] to Partial<KeyValueMapping[TOnyxKey]> we lose all type equality check between the components' OnyxProps and withOnyx mapping, which means that we could type anything in the Onyx prop and the selector wouldn't be able to check if its return type matches with the corresponding Onyx prop.

  2. Also, by doing Partial<KeyValueMapping[TOnyxKey]> you are limiting the selector's return to be an object with optional properties of that Onyx value, which is not always the case.

    When we have a combination of a collection key and a selector, the withOnyx will run selector function for each entry of that collection and build a collection object with the results of all selector executions, for example:

    type ContactMethodDetailsPageOnyxProps = {
        policiesIDs: OnyxCollection<string>;
    };
    
    ...
    
    export default withOnyx<ContactMethodDetailsPageProps, ContactMethodDetailsPageOnyxProps>({
        policiesIDs: {
            key: ONYXKEYS.COLLECTION.POLICY,
            selector: (data) => data?.id ?? '', // this is valid and will make policies return a collection of ids
        },
    })(ContactMethodDetailsPage);

To improve these typings, my suggestion is:

  1. Add this type in lib/types.d.ts which will make possible extracting the value of a Onyx collection:
    ...
    type OnyxCollection<TOnyxValue> = OnyxEntry<Record<string, TOnyxValue | null>>;
    
    /**
     * Utility type to extract `TOnyxValue` from `OnyxCollection<TOnyxValue>`.
     */
    type ExtractOnyxCollectionValue<TOnyxCollection> = TOnyxCollection extends NonNullable<OnyxCollection<infer U>> ? U : never;
    ...
  2. Use it in CollectionMapping:
    /**
     * Represents the mapping options between an Onyx collection key without suffix and the component's prop with all its possibilities.
     */
    type CollectionMapping<TComponentProps, TOnyxProps, TOnyxProp extends keyof TOnyxProps, TOnyxKey extends CollectionKeyBase> = BaseMapping<TComponentProps, TOnyxProps> &
        CollectionBaseMapping<TOnyxKey> &
        (
            | BaseMappingKey<TComponentProps, TOnyxProps, TOnyxProp, TOnyxKey, OnyxCollection<KeyValueMapping[TOnyxKey]>>
            | BaseMappingStringKeyAndSelector<TComponentProps, TOnyxProps, ExtractOnyxCollectionValue<TOnyxProps[TOnyxProp]>, TOnyxKey>
            | BaseMappingFunctionKeyAndSelector<TComponentProps, TOnyxProps, ExtractOnyxCollectionValue<TOnyxProps[TOnyxProp]>, TOnyxKey>
        );

This way we can ensure the type equality checks will still work and get rid of these issues when using the selector. For your case in this discussion, final solution would be like this:

type ContactMethodDetailsPageOnyxProps = {
    ...
    policies: OnyxCollection<Pick<Policy, 'id' | 'ownerAccountID'>>;
};

...

export default withOnyx<ContactMethodDetailsPageProps, ContactMethodDetailsPageOnyxProps>({
    ...
    policies: {
        key: ONYXKEYS.COLLECTION.POLICY,
        selector: (data) => ({id: data?.id ?? '', ownerAccountID: data?.ownerAccountID}),
    },
})(ContactMethodDetailsPage);

);

/**
Expand Down
Loading