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

[TS migration] Migrate withOnyx.js to TypeScript #542

Merged
merged 16 commits into from
May 29, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
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
6 changes: 4 additions & 2 deletions API-INTERNAL.md
Original file line number Diff line number Diff line change
Expand Up @@ -115,7 +115,8 @@ whatever it is we attempted to do.</p>
</dd>
<dt><a href="#removeNullValues">removeNullValues()</a> ⇒</dt>
<dd><p>Removes a key from storage if the value is null.
Otherwise removes all nested null values in objects and returns the object</p>
Otherwise removes all nested null values in objects,
if shouldRemoveNestedNulls is true and returns the object.</p>
</dd>
<dt><a href="#prepareKeyValuePairsForStorage">prepareKeyValuePairsForStorage()</a> ⇒</dt>
<dd><p>Storage expects array like: [[&quot;@MyApp_user&quot;, value_1], [&quot;@MyApp_key&quot;, value_2]]
Expand Down Expand Up @@ -367,7 +368,8 @@ Notifies subscribers and writes current value to cache

## removeNullValues() ⇒
Removes a key from storage if the value is null.
Otherwise removes all nested null values in objects and returns the object
Otherwise removes all nested null values in objects,
if shouldRemoveNestedNulls is true and returns the object.

**Kind**: global function
**Returns**: The value without null values and a boolean "wasRemoved", which indicates if the key got removed completely
Expand Down
28 changes: 14 additions & 14 deletions lib/OnyxUtils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,32 +3,32 @@
import {deepEqual} from 'fast-equals';
import lodashClone from 'lodash/clone';
import type {ValueOf} from 'type-fest';
import DevTools from './DevTools';
import * as Logger from './Logger';
import type Onyx from './Onyx';
import cache from './OnyxCache';
import * as Str from './Str';
import * as PerformanceUtils from './PerformanceUtils';
import Storage from './storage';
import utils from './utils';
import * as Str from './Str';
import unstable_batchedUpdates from './batch';
import DevTools from './DevTools';
import Storage from './storage';
import type {
DeepRecord,
Mapping,
CollectionKey,
CollectionKeyBase,
DeepRecord,
DefaultConnectCallback,
DefaultConnectOptions,
KeyValueMapping,
Mapping,
NullableKeyValueMapping,
OnyxCollection,
OnyxEntry,
OnyxKey,
OnyxValue,
Selector,
WithOnyxInstanceState,
OnyxCollection,
WithOnyxConnectOptions,
DefaultConnectOptions,
OnyxEntry,
KeyValueMapping,
DefaultConnectCallback,
} from './types';
import type Onyx from './Onyx';
import utils from './utils';
import type {WithOnyxState} from './withOnyx/types';

// Method constants
const METHOD = {
Expand Down Expand Up @@ -195,7 +195,7 @@ function batchUpdates(updates: () => void): Promise<void> {
function reduceCollectionWithSelector<TKey extends CollectionKeyBase, TMap, TReturn>(
collection: OnyxCollection<KeyValueMapping[TKey]>,
selector: Selector<TKey, TMap, TReturn>,
withOnyxInstanceState: WithOnyxInstanceState<TMap> | undefined,
withOnyxInstanceState: WithOnyxState<TMap> | undefined,
): Record<string, TReturn> {
return Object.entries(collection ?? {}).reduce((finalCollection: Record<string, TReturn>, [key, item]) => {
// eslint-disable-next-line no-param-reassign
Expand Down
25 changes: 13 additions & 12 deletions lib/index.ts
Original file line number Diff line number Diff line change
@@ -1,25 +1,26 @@
import type {ConnectOptions, OnyxUpdate} from './Onyx';
import Onyx from './Onyx';
import type {OnyxUpdate, ConnectOptions} from './Onyx';
import type {CustomTypeOptions, OnyxCollection, OnyxEntry, NullishDeep, KeyValueMapping, OnyxKey, Selector, WithOnyxInstanceState, OnyxValue} from './types';
import type {UseOnyxResult, FetchStatus, ResultMetadata} from './useOnyx';
import type {CustomTypeOptions, KeyValueMapping, NullishDeep, OnyxCollection, OnyxEntry, OnyxKey, OnyxValue, Selector} from './types';
import type {FetchStatus, ResultMetadata, UseOnyxResult} from './useOnyx';
import useOnyx from './useOnyx';
import withOnyx from './withOnyx';
import type {WithOnyxState} from './withOnyx/types';

export default Onyx;
export {withOnyx, useOnyx};
export {useOnyx, withOnyx};
export type {
ConnectOptions,
CustomTypeOptions,
FetchStatus,
KeyValueMapping,
NullishDeep,
OnyxCollection,
OnyxEntry,
OnyxUpdate,
ConnectOptions,
NullishDeep,
KeyValueMapping,
OnyxKey,
Selector,
WithOnyxInstanceState,
UseOnyxResult,
OnyxUpdate,
OnyxValue,
FetchStatus,
ResultMetadata,
Selector,
UseOnyxResult,
WithOnyxState,
};
20 changes: 6 additions & 14 deletions lib/types.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import type {Component} from 'react';
import type {Merge} from 'type-fest';
import type {BuiltIns} from 'type-fest/source/internal';
import type OnyxUtils from './OnyxUtils';
import type {WithOnyxInstance, WithOnyxState} from './withOnyx/types';

/**
* Utility type that excludes `null` from the type `TValue`.
Expand Down Expand Up @@ -148,7 +148,7 @@ type NullableKeyValueMapping = {
* 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, TOnyxProps, TReturnType> = (value: OnyxEntry<KeyValueMapping[TKey]>, state: WithOnyxInstanceState<TOnyxProps>) => TReturnType;
type Selector<TKey extends OnyxKey, TOnyxProps, TReturnType> = (value: OnyxEntry<KeyValueMapping[TKey]>, state?: WithOnyxState<TOnyxProps>) => TReturnType;

/**
* Represents a single Onyx entry, that can be either `TOnyxValue` or `null` / `undefined` if it doesn't exist.
Expand Down Expand Up @@ -252,11 +252,6 @@ type NullishObjectDeep<ObjectType extends object> = {
[KeyType in keyof ObjectType]?: NullishDeep<ObjectType[KeyType]> | null;
};

/**
* Represents withOnyx's internal state, containing the Onyx props and a `loading` flag.
*/
type WithOnyxInstanceState<TOnyxProps> = (TOnyxProps & {loading: boolean}) | undefined;

/**
* Represents a mapping between Onyx collection keys and their respective values.
*
Expand All @@ -274,11 +269,6 @@ type Collection<TKey extends CollectionKeyBase, TMap, TValue> = {
: never;
};

type WithOnyxInstance = Component<unknown, WithOnyxInstanceState<NullableKeyValueMapping>> & {
setStateProxy: (cb: (state: Record<string, OnyxCollection<KeyValueMapping[OnyxKey]>>) => OnyxValue<OnyxKey>) => void;
setWithOnyxState: (statePropertyName: OnyxKey, value: OnyxValue<OnyxKey>) => void;
};

/** Represents the base options used in `Onyx.connect()` method. */
type BaseConnectOptions = {
initWithStoredValues?: boolean;
Expand Down Expand Up @@ -400,6 +390,9 @@ type InitOptions = {
debugSetState?: boolean;
};

// eslint-disable-next-line @typescript-eslint/no-explicit-any
type GenericFunction = (...args: any[]) => any;

export type {
BaseConnectOptions,
Collection,
Expand All @@ -413,6 +406,7 @@ export type {
DefaultConnectCallback,
DefaultConnectOptions,
ExtractOnyxCollectionValue,
GenericFunction,
InitOptions,
Key,
KeyValueMapping,
Expand All @@ -428,6 +422,4 @@ export type {
OnyxValue,
Selector,
WithOnyxConnectOptions,
WithOnyxInstance,
WithOnyxInstanceState,
};
6 changes: 6 additions & 0 deletions lib/types/modules/react.d.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
import type React from 'react';

declare module 'react' {
// eslint-disable-next-line @typescript-eslint/ban-types
function forwardRef<T, P = {}>(render: (props: P, ref: React.ForwardedRef<T>) => React.ReactElement | null): (props: P & React.RefAttributes<T>) => React.ReactElement | null;
}
56 changes: 55 additions & 1 deletion lib/utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -138,4 +138,58 @@ function checkCompatibilityWithExistingValue(value: unknown, existingValue: unkn
};
}

export default {isEmptyObject, fastMerge, formatActionName, removeNestedNullValues, checkCompatibilityWithExistingValue};
/**
* Filters an object based on a condition and an inclusion flag.
*
* @param obj - The object to filter.
* @param condition - The condition to apply.
* @param include - If true, include entries that match the condition; otherwise, exclude them.
* @returns The filtered object.
*/
function filterObject<TValue>(obj: Record<string, TValue>, condition: string | string[] | ((entry: [string, TValue]) => boolean), include: boolean): Record<string, TValue> {
const result: Record<string, TValue> = {};
const entries = Object.entries(obj);

for (let i = 0; i < entries.length; i++) {
const [key, value] = entries[i];
let shouldInclude: boolean;

if (Array.isArray(condition)) {
shouldInclude = condition.includes(key);
} else if (typeof condition === 'string') {
shouldInclude = key === condition;
} else {
shouldInclude = condition(entries[i]);
}

if (include ? shouldInclude : !shouldInclude) {
result[key] = value;
}
}
fabioh8010 marked this conversation as resolved.
Show resolved Hide resolved

return result;
}

/**
* Picks entries from an object based on a condition.
*
* @param obj - The object to pick entries from.
* @param condition - The condition to determine which entries to pick.
* @returns The object containing only the picked entries.
*/
function pick<TValue>(obj: Record<string, TValue>, condition: string | string[] | ((entry: [string, TValue]) => boolean)): Record<string, TValue> {
return filterObject(obj, condition, true);
}

/**
* Omits entries from an object based on a condition.
*
* @param obj - The object to omit entries from.
* @param condition - The condition to determine which entries to omit.
* @returns The object containing only the remaining entries after omission.
*/
function omit<TValue>(obj: Record<string, TValue>, condition: string | string[] | ((entry: [string, TValue]) => boolean)): Record<string, TValue> {
return filterObject(obj, condition, false);
}

export default {isEmptyObject, fastMerge, formatActionName, removeNestedNullValues, checkCompatibilityWithExistingValue, pick, omit};
Loading
Loading