diff --git a/packages/react-native/Libraries/ActionSheetIOS/ActionSheetIOS.js b/packages/react-native/Libraries/ActionSheetIOS/ActionSheetIOS.js index 4cb8fcf51460..bac068ea8053 100644 --- a/packages/react-native/Libraries/ActionSheetIOS/ActionSheetIOS.js +++ b/packages/react-native/Libraries/ActionSheetIOS/ActionSheetIOS.js @@ -4,7 +4,7 @@ * This source code is licensed under the MIT license found in the * LICENSE file in the root directory of this source tree. * - * @flow + * @flow strict-local * @format */ @@ -49,6 +49,7 @@ export type ShareActionSheetIOSOptions = Readonly<{ export type ShareActionSheetError = Readonly<{ domain: string, code: string, + // $FlowFixMe[unclear-type] userInfo?: ?Object, message: string, }>; @@ -155,7 +156,9 @@ const ActionSheetIOS = { */ showShareActionSheetWithOptions( options: ShareActionSheetIOSOptions, + // $FlowFixMe[unclear-type] failureCallback: Function | ((error: ShareActionSheetError) => void), + // $FlowFixMe[unclear-type] successCallback: Function | ((success: boolean, method: ?string) => void), ) { invariant( diff --git a/packages/react-native/Libraries/Alert/Alert.js b/packages/react-native/Libraries/Alert/Alert.js index 4130ec511438..0efac6d6beda 100644 --- a/packages/react-native/Libraries/Alert/Alert.js +++ b/packages/react-native/Libraries/Alert/Alert.js @@ -4,7 +4,7 @@ * This source code is licensed under the MIT license found in the * LICENSE file in the root directory of this source tree. * - * @flow + * @flow strict-local * @format */ @@ -26,6 +26,7 @@ export type AlertButtonStyle = 'default' | 'cancel' | 'destructive'; export type AlertButton = { text?: string, + // $FlowFixMe[unclear-type] onPress?: ?((value?: string) => any) | ?Function, isPreferred?: boolean, style?: AlertButtonStyle, @@ -121,7 +122,7 @@ class Alert { cancelable: false, }; - if (options && options.cancelable) { + if (options != null && options.cancelable === true) { config.cancelable = options.cancelable; } // At most three buttons (neutral, negative, positive). Ignore rest. @@ -141,25 +142,20 @@ class Alert { config.buttonNegative = buttonNegative.text || ''; } if (buttonPositive) { - config.buttonPositive = buttonPositive.text || defaultPositiveText; + config.buttonPositive = + buttonPositive.text != null && buttonPositive.text !== '' + ? buttonPositive.text + : defaultPositiveText; } - /* $FlowFixMe[missing-local-annot] The type annotation(s) required by - * Flow's LTI update could not be added via codemod */ - const onAction = (action, buttonKey) => { + const onAction = (action: string, buttonKey?: number) => { if (action === constants.buttonClicked) { if (buttonKey === constants.buttonNeutral) { - // $FlowFixMe[incompatible-type] - // $FlowFixMe[incompatible-use] - buttonNeutral.onPress && buttonNeutral.onPress(); + buttonNeutral?.onPress?.(); } else if (buttonKey === constants.buttonNegative) { - // $FlowFixMe[incompatible-type] - // $FlowFixMe[incompatible-use] - buttonNegative.onPress && buttonNegative.onPress(); + buttonNegative?.onPress?.(); } else if (buttonKey === constants.buttonPositive) { - // $FlowFixMe[incompatible-type] - // $FlowFixMe[incompatible-use] - buttonPositive.onPress && buttonPositive.onPress(); + buttonPositive?.onPress?.(); } } else if (action === constants.dismissed) { options && options.onDismiss && options.onDismiss(); @@ -186,7 +182,7 @@ class Alert { options?: AlertOptions, ): void { if (Platform.OS === 'ios') { - let callbacks: Array = []; + let callbacks: Array unknown> = []; const buttons = []; let cancelButtonKey; let destructiveButtonKey; @@ -195,16 +191,20 @@ class Alert { callbacks = [callbackOrButtons]; } else if (Array.isArray(callbackOrButtons)) { callbackOrButtons.forEach((btn, index) => { - callbacks[index] = btn.onPress; + callbacks[index] = + btn.onPress == null ? null : value => btn.onPress?.(value); if (btn.style === 'cancel') { cancelButtonKey = String(index); } else if (btn.style === 'destructive') { destructiveButtonKey = String(index); } - if (btn.isPreferred) { + if (btn.isPreferred === true) { preferredButtonKey = String(index); } - if (btn.text || index < (callbackOrButtons || []).length - 1) { + if ( + (btn.text != null && btn.text !== '') || + index < callbackOrButtons.length - 1 + ) { const btnDef: {[number]: string} = {}; btnDef[index] = btn.text || ''; buttons.push(btnDef); @@ -215,7 +215,7 @@ class Alert { alertWithArgs( { title: title || '', - message: message || undefined, + message: message != null && message !== '' ? message : undefined, buttons, type: type || undefined, defaultValue, diff --git a/packages/react-native/Libraries/Components/Touchable/PooledClass.js b/packages/react-native/Libraries/Components/Touchable/PooledClass.js index 224c17bd295e..d831176f65af 100644 --- a/packages/react-native/Libraries/Components/Touchable/PooledClass.js +++ b/packages/react-native/Libraries/Components/Touchable/PooledClass.js @@ -4,7 +4,7 @@ * This source code is licensed under the MIT license found in the * LICENSE file in the root directory of this source tree. * - * @flow + * @flow strict-local * @format */ @@ -20,6 +20,7 @@ import invariant from 'invariant'; */ /* $FlowFixMe[missing-this-annot] The 'this' type annotation(s) required by * Flow's LTI update could not be added via codemod */ +// $FlowFixMe[unclear-type] const oneArgumentPooler = function (copyFieldsFrom: any) { const Klass = this; // eslint-disable-line consistent-this if (Klass.instancePool.length) { @@ -33,6 +34,7 @@ const oneArgumentPooler = function (copyFieldsFrom: any) { /* $FlowFixMe[missing-this-annot] The 'this' type annotation(s) required by * Flow's LTI update could not be added via codemod */ +// $FlowFixMe[unclear-type] const twoArgumentPooler = function (a1: any, a2: any) { const Klass = this; // eslint-disable-line consistent-this if (Klass.instancePool.length) { @@ -46,6 +48,7 @@ const twoArgumentPooler = function (a1: any, a2: any) { /* $FlowFixMe[missing-this-annot] The 'this' type annotation(s) required by * Flow's LTI update could not be added via codemod */ +// $FlowFixMe[unclear-type] const threeArgumentPooler = function (a1: any, a2: any, a3: any) { const Klass = this; // eslint-disable-line consistent-this if (Klass.instancePool.length) { @@ -59,6 +62,7 @@ const threeArgumentPooler = function (a1: any, a2: any, a3: any) { /* $FlowFixMe[missing-this-annot] The 'this' type annotation(s) required by * Flow's LTI update could not be added via codemod */ +// $FlowFixMe[unclear-type] const fourArgumentPooler = function (a1: any, a2: any, a3: any, a4: any) { const Klass = this; // eslint-disable-line consistent-this if (Klass.instancePool.length) { @@ -89,6 +93,7 @@ const standardReleaser = function (instance) { const DEFAULT_POOL_SIZE = 10; const DEFAULT_POOLER = oneArgumentPooler; +// $FlowFixMe[unclear-type] type Pooler = any; /** @@ -112,6 +117,7 @@ const addPoolingTo = function ( } { // Casting as any so that flow ignores the actual implementation and trusts // it to match the type we declared + // $FlowFixMe[unclear-type] const NewKlass: any = CopyConstructor; NewKlass.instancePool = []; NewKlass.getPooled = pooler || DEFAULT_POOLER; diff --git a/packages/react-native/Libraries/Core/Timers/JSTimers.js b/packages/react-native/Libraries/Core/Timers/JSTimers.js index 82bd3c81de00..f196f54015c5 100644 --- a/packages/react-native/Libraries/Core/Timers/JSTimers.js +++ b/packages/react-native/Libraries/Core/Timers/JSTimers.js @@ -4,7 +4,7 @@ * This source code is licensed under the MIT license found in the * LICENSE file in the root directory of this source tree. * - * @flow + * @flow strict-local * @format * @deprecated */ @@ -29,13 +29,15 @@ export type JSTimerType = | 'queueReactNativeMicrotask' | 'requestIdleCallback'; +type TimerFunc = (...args: Array) => unknown; + // These timing constants should be kept in sync with the ones in native ios and // android `RCTTiming` module. const FRAME_DURATION = 1000 / 60; const IDLE_CALLBACK_FRAME_DEADLINE = 1; // Parallel arrays -const callbacks: Array = []; +const callbacks: Array = []; const types: Array = []; const timerIDs: Array = []; const freeIdxs: Array = []; @@ -57,7 +59,7 @@ function _getFreeIndex(): number { return freeIdx; } -function _allocateCallback(func: Function, type: JSTimerType): number { +function _allocateCallback(func: TimerFunc, type: JSTimerType): number { const id = GUID++; const freeIndex = _getFreeIndex(); timerIDs[freeIndex] = id; @@ -210,9 +212,9 @@ const JSTimers = { * @param {number} duration Number of milliseconds. */ setTimeout: function ( - func: Function, + func: TimerFunc, duration: number, - ...args: any + ...args: Array ): number { const id = _allocateCallback( () => func.apply(undefined, args), @@ -227,9 +229,9 @@ const JSTimers = { * @param {number} duration Number of milliseconds. */ setInterval: function ( - func: Function, + func: TimerFunc, duration: number, - ...args: any + ...args: Array ): number { const id = _allocateCallback( () => func.apply(undefined, args), @@ -247,7 +249,10 @@ const JSTimers = { * @param {function} func Callback to be invoked before the end of the * current JavaScript execution loop. */ - queueReactNativeMicrotask: function (func: Function, ...args: any): number { + queueReactNativeMicrotask: function ( + func: TimerFunc, + ...args: Array + ): number { const id = _allocateCallback( () => func.apply(undefined, args), 'queueReactNativeMicrotask', @@ -259,7 +264,7 @@ const JSTimers = { /** * @param {function} func Callback to be invoked every frame. */ - requestAnimationFrame: function (func: Function): any | number { + requestAnimationFrame: function (func: TimerFunc): number { const id = _allocateCallback(func, 'requestAnimationFrame'); createTimer(id, 1, Date.now(), /* recurring */ false); return id; @@ -271,9 +276,9 @@ const JSTimers = { * @param {?object} options */ requestIdleCallback: function ( - func: Function, - options: ?Object, - ): any | number { + func: TimerFunc, + options: ?{timeout?: number, ...}, + ): number { if (requestIdleCallbacks.length === 0) { setSendIdleEvents(true); } @@ -281,7 +286,7 @@ const JSTimers = { const timeout = options && options.timeout; const id: number = _allocateCallback( timeout != null - ? (deadline: any) => { + ? (deadline: unknown) => { const timeoutId: number = requestIdleCallbackTimeouts[id]; if (timeoutId) { JSTimers.clearTimeout(timeoutId); @@ -353,7 +358,7 @@ const JSTimers = { * This is called from the native side. We are passed an array of timerIDs, * and */ - callTimers: function (timersToCall: Array): any | void { + callTimers: function (timersToCall: Array): void { invariant( timersToCall.length !== 0, 'Cannot call `callTimers` with an empty list of IDs.', @@ -458,20 +463,34 @@ function setSendIdleEvents(sendIdleEvents: boolean): void { } let ExportedJSTimers: { - callIdleCallbacks: (frameTime: number) => any | void, + callIdleCallbacks: (frameTime: number) => void, callReactNativeMicrotasks: () => void, - callTimers: (timersToCall: Array) => any | void, + callTimers: (timersToCall: Array) => void, cancelAnimationFrame: (timerID: number) => void, cancelIdleCallback: (timerID: number) => void, clearReactNativeMicrotask: (timerID: number) => void, clearInterval: (timerID: number) => void, clearTimeout: (timerID: number) => void, - emitTimeDriftWarning: (warningMessage: string) => any | void, - requestAnimationFrame: (func: any) => any | number, - requestIdleCallback: (func: any, options: ?any) => any | number, - queueReactNativeMicrotask: (func: any, ...args: any) => number, - setInterval: (func: any, duration: number, ...args: any) => number, - setTimeout: (func: any, duration: number, ...args: any) => number, + emitTimeDriftWarning: (warningMessage: string) => void, + requestAnimationFrame: (func: TimerFunc) => number, + requestIdleCallback: ( + func: TimerFunc, + options: ?{timeout?: number, ...}, + ) => number, + queueReactNativeMicrotask: ( + func: TimerFunc, + ...args: Array + ) => number, + setInterval: ( + func: TimerFunc, + duration: number, + ...args: Array + ) => number, + setTimeout: ( + func: TimerFunc, + duration: number, + ...args: Array + ) => number, }; if (!NativeTiming) { diff --git a/packages/react-native/Libraries/Core/setUpReactDevTools.js b/packages/react-native/Libraries/Core/setUpReactDevTools.js index 0e5a7aeb6779..f5bc1ccf7bf7 100644 --- a/packages/react-native/Libraries/Core/setUpReactDevTools.js +++ b/packages/react-native/Libraries/Core/setUpReactDevTools.js @@ -4,13 +4,16 @@ * This source code is licensed under the MIT license found in the * LICENSE file in the root directory of this source tree. * - * @flow + * @flow strict-local * @format */ 'use strict'; -import type {Domain} from '../../src/private/devsupport/rndevtools/setUpFuseboxReactDevToolsDispatcher'; +import type { + Domain, + JSONValue, +} from '../../src/private/devsupport/rndevtools/setUpFuseboxReactDevToolsDispatcher'; import type {Spec as NativeReactDevToolsRuntimeSettingsModuleSpec} from '../../src/private/devsupport/rndevtools/specs/NativeReactDevToolsRuntimeSettingsModule'; if (__DEV__) { @@ -35,6 +38,7 @@ if (__DEV__) { const { initialize, connectWithCustomMessagingProtocol, + // $FlowFixMe[untyped-import] } = require('react-devtools-core'); const reactDevToolsSettingsManager = require('../../src/private/devsupport/rndevtools/ReactDevToolsSettingsManager'); @@ -73,7 +77,7 @@ if (__DEV__) { require('../Components/View/ReactNativeStyleAttributes').default; const resolveRNStyle = require('../StyleSheet/flattenStyle').default; - function handleReactDevToolsSettingsUpdate(settings: Object) { + function handleReactDevToolsSettingsUpdate(settings: {[string]: unknown}) { reactDevToolsSettingsManager.setGlobalHookSettings( JSON.stringify(settings), ); @@ -97,13 +101,13 @@ if (__DEV__) { maybeReactDevToolsRuntimeSettingsModuleModule, ); disconnect = connectWithCustomMessagingProtocol({ - onSubscribe: listener => { + onSubscribe: (listener: (message: JSONValue) => void) => { domain.onMessage.addEventListener(listener); }, - onUnsubscribe: listener => { + onUnsubscribe: (listener: (message: JSONValue) => void) => { domain.onMessage.removeEventListener(listener); }, - onMessage: (event, payload) => { + onMessage: (event: string, payload: JSONValue) => { domain.sendMessage({event, payload}); }, nativeStyleEditorValidAttributes: Object.keys(ReactNativeStyleAttributes), diff --git a/packages/react-native/Libraries/Core/setUpReactRefresh.js b/packages/react-native/Libraries/Core/setUpReactRefresh.js index be8844dcf344..7c4a272fd73b 100644 --- a/packages/react-native/Libraries/Core/setUpReactRefresh.js +++ b/packages/react-native/Libraries/Core/setUpReactRefresh.js @@ -4,7 +4,7 @@ * This source code is licensed under the MIT license found in the * LICENSE file in the root directory of this source tree. * - * @flow + * @flow strict-local * @format */ @@ -18,6 +18,7 @@ if (__DEV__) { } // This needs to run before the renderer initializes. + // $FlowFixMe[untyped-import] const ReactRefreshRuntime = require('react-refresh/runtime'); ReactRefreshRuntime.injectIntoGlobalHook(global); diff --git a/packages/react-native/Libraries/Lists/FlatList.js b/packages/react-native/Libraries/Lists/FlatList.js index ff4563be2406..bc4d1363931f 100644 --- a/packages/react-native/Libraries/Lists/FlatList.js +++ b/packages/react-native/Libraries/Lists/FlatList.js @@ -4,7 +4,7 @@ * This source code is licensed under the MIT license found in the * LICENSE file in the root directory of this source tree. * - * @flow + * @flow strict-local * @format */ @@ -78,10 +78,10 @@ type OptionalFlatListProps = { * any of your `renderItem`, Header, Footer, etc. functions depend on anything outside of the * `data` prop, stick it here and treat it immutably. */ - extraData?: any, + extraData?: unknown, /** * `getItemLayout` is an optional optimizations that let us skip measurement of dynamic content if - * you know the height of items a priori. `getItemLayout` is the most efficient, and is easy to + * you know the height of items a priori. * use if you have fixed height items, for example: * * getItemLayout={(data, index) => ( @@ -172,9 +172,10 @@ function numColumnsOrDefault(numColumns: ?number) { return numColumns ?? 1; } -function isArrayLike(data: unknown): boolean { - // $FlowExpectedError[incompatible-use] - return typeof Object(data).length === 'number'; +function isArrayLike( + data: ?Readonly<$ArrayLike>, +): implies data is Readonly<$ArrayLike> { + return data != null && typeof data.length === 'number'; } type FlatListBaseProps = { @@ -305,6 +306,7 @@ export type FlatListProps = Readonly<{ * * Also inherits [ScrollView Props](docs/scrollview.html#props), unless it is nested in another FlatList of same orientation. */ +// flowlint-next-line unclear-type:off class FlatList extends React.PureComponent> { /** * Scrolls to the end of the content. May be janky without `getItemLayout` prop. @@ -404,7 +406,7 @@ class FlatList extends React.PureComponent> { } } - getScrollableNode(): any { + getScrollableNode(): ?number { if (this._listRef) { return this._listRef.getScrollableNode(); } @@ -499,7 +501,10 @@ class FlatList extends React.PureComponent> { 'FlatList does not support custom data formats.', ); if (numColumns > 1) { - invariant(!horizontal, 'numColumns does not support horizontal.'); + invariant( + !Boolean(horizontal), + 'numColumns does not support horizontal.', + ); } else { invariant( !columnWrapperStyle, @@ -611,11 +616,13 @@ class FlatList extends React.PureComponent> { } _renderer = ( - ListItemComponent: ?(React.ComponentType | React.MixedElement), + ListItemComponent: ?( + React.ComponentType> | React.MixedElement + ), renderItem: ?ListRenderItem, columnWrapperStyle: ?ViewStyleProp, numColumns: ?number, - extraData: ?any, + extraData: ?unknown, // $FlowFixMe[missing-local-annot] ) => { const cols = numColumnsOrDefault(numColumns); diff --git a/packages/react-native/Libraries/Lists/SectionList.js b/packages/react-native/Libraries/Lists/SectionList.js index 9aa96b248aaa..f75070f25e66 100644 --- a/packages/react-native/Libraries/Lists/SectionList.js +++ b/packages/react-native/Libraries/Lists/SectionList.js @@ -4,7 +4,7 @@ * This source code is licensed under the MIT license found in the * LICENSE file in the root directory of this source tree. * - * @flow + * @flow strict-local * @format */ @@ -26,6 +26,7 @@ import * as React from 'react'; const VirtualizedSectionList = VirtualizedLists.VirtualizedSectionList; type DefaultSectionT = { + // flowlint-next-line unclear-type:off [key: string]: any, }; @@ -74,7 +75,7 @@ type OptionalSectionListProps = { * any of your `renderItem`, Header, Footer, etc. functions depend on anything outside of the * `data` prop, stick it here and treat it immutably. */ - extraData?: any, + extraData?: unknown, /** * How many items to render in the initial batch. This should be enough to fill the screen but not * much more. Note these items will never be unmounted as part of the windowed rendering in order @@ -172,6 +173,7 @@ export type SectionListProps = { * @see https://reactnative.dev/docs/sectionlist */ export default class SectionList< + // flowlint-next-line unclear-type:off ItemT = any, SectionT = DefaultSectionT, > extends React.PureComponent> { @@ -226,14 +228,14 @@ export default class SectionList< /** * Provides a handle to the underlying scroll node. */ - getScrollableNode(): any { + getScrollableNode(): ?number { const listRef = this._wrapperListRef && this._wrapperListRef.getListRef(); if (listRef) { return listRef.getScrollableNode(); } } - setNativeProps(props: Object) { + setNativeProps(props: {[string]: unknown, ...}) { const listRef = this._wrapperListRef && this._wrapperListRef.getListRef(); if (listRef) { listRef.setNativeProps(props); diff --git a/packages/react-native/Libraries/Lists/SectionListModern.js b/packages/react-native/Libraries/Lists/SectionListModern.js deleted file mode 100644 index 2661a31bc326..000000000000 --- a/packages/react-native/Libraries/Lists/SectionListModern.js +++ /dev/null @@ -1,248 +0,0 @@ -/** - * Copyright (c) Meta Platforms, Inc. and affiliates. - * - * This source code is licensed under the MIT license found in the - * LICENSE file in the root directory of this source tree. - * - * @flow - * @format - */ - -'use strict'; - -import type {ScrollResponderType} from '../Components/ScrollView/ScrollView'; -import type { - ScrollToLocationParamsType, - SectionBase as _SectionBase, - SectionData, - VirtualizedSectionListProps, -} from '@react-native/virtualized-lists'; - -import Platform from '../Utilities/Platform'; -import VirtualizedLists from '@react-native/virtualized-lists'; -import * as React from 'react'; -import {useImperativeHandle, useRef} from 'react'; - -const VirtualizedSectionList = VirtualizedLists.VirtualizedSectionList; - -type DefaultSectionT = { - [key: string]: any, -}; - -export type SectionBase< - SectionItemT, - SectionT = DefaultSectionT, -> = _SectionBase; - -type RequiredProps = { - /** - * The actual data to render, akin to the `data` prop in [``](https://reactnative.dev/docs/flatlist). - * - * General shape: - * - * sections: $ReadOnlyArray<{ - * data: $ReadOnlyArray, - * renderItem?: ({item: SectionItem, ...}) => ?React.MixedElement, - * ItemSeparatorComponent?: ?ReactClass<{highlighted: boolean, ...}>, - * }> - */ - sections: ReadonlyArray>, -}; - -type OptionalProps = { - /** - * Default renderer for every item in every section. Can be over-ridden on a per-section basis. - */ - renderItem?: (info: { - item: ItemT, - index: number, - section: SectionData, - separators: { - highlight: () => void, - unhighlight: () => void, - updateProps: (select: 'leading' | 'trailing', newProps: Object) => void, - ... - }, - ... - }) => null | React.MixedElement, - /** - * A marker property for telling the list to re-render (since it implements `PureComponent`). If - * any of your `renderItem`, Header, Footer, etc. functions depend on anything outside of the - * `data` prop, stick it here and treat it immutably. - */ - extraData?: any, - /** - * How many items to render in the initial batch. This should be enough to fill the screen but not - * much more. Note these items will never be unmounted as part of the windowed rendering in order - * to improve perceived performance of scroll-to-top actions. - */ - initialNumToRender?: ?number, - /** - * Reverses the direction of scroll. Uses scale transforms of -1. - */ - inverted?: ?boolean, - /** - * Used to extract a unique key for a given item at the specified index. Key is used for caching - * and as the react key to track item re-ordering. The default extractor checks item.key, then - * falls back to using the index, like react does. Note that this sets keys for each item, but - * each overall section still needs its own key. - */ - keyExtractor?: ?(item: ItemT, index: number) => string, - /** - * Called once when the scroll position gets within `onEndReachedThreshold` of the rendered - * content. - */ - onEndReached?: ?(info: {distanceFromEnd: number, ...}) => void, - /** - * Note: may have bugs (missing content) in some circumstances - use at your own risk. - * - * This may improve scroll performance for large lists. - */ - removeClippedSubviews?: boolean, -}; - -export type Props = Readonly<{ - ...Omit< - VirtualizedSectionListProps, - 'getItem' | 'getItemCount' | 'renderItem' | 'keyExtractor', - >, - ...RequiredProps, - ...OptionalProps, -}>; - -/** - * A performant interface for rendering sectioned lists, supporting the most handy features: - * - * - Fully cross-platform. - * - Configurable viewability callbacks. - * - List header support. - * - List footer support. - * - Item separator support. - * - Section header support. - * - Section separator support. - * - Heterogeneous data and item rendering support. - * - Pull to Refresh. - * - Scroll loading. - * - * If you don't need section support and want a simpler interface, use - * [``](https://reactnative.dev/docs/flatlist). - * - * Simple Examples: - * - * } - * renderSectionHeader={({section}) =>
} - * sections={[ // homogeneous rendering between sections - * {data: [...], title: ...}, - * {data: [...], title: ...}, - * {data: [...], title: ...}, - * ]} - * /> - * - * - * - * This is a convenience wrapper around [``](docs/virtualizedlist), - * and thus inherits its props (as well as those of `ScrollView`) that aren't explicitly listed - * here, along with the following caveats: - * - * - Internal state is not preserved when content scrolls out of the render window. Make sure all - * your data is captured in the item data or external stores like Flux, Redux, or Relay. - * - This is a `PureComponent` which means that it will not re-render if `props` remain shallow- - * equal. Make sure that everything your `renderItem` function depends on is passed as a prop - * (e.g. `extraData`) that is not `===` after updates, otherwise your UI may not update on - * changes. This includes the `data` prop and parent component state. - * - In order to constrain memory and enable smooth scrolling, content is rendered asynchronously - * offscreen. This means it's possible to scroll faster than the fill rate and momentarily see - * blank content. This is a tradeoff that can be adjusted to suit the needs of each application, - * and we are working on improving it behind the scenes. - * - By default, the list looks for a `key` prop on each item and uses that for the React key. - * Alternatively, you can provide a custom `keyExtractor` prop. - * - */ -const SectionList: component( - ref?: React.RefSetter, - ...Props -) = ({ - ref, - ...props -}: { - ref?: React.RefSetter, - ...Props, -}) => { - const propsWithDefaults = { - stickySectionHeadersEnabled: Platform.OS === 'ios', - ...props, - }; - - const wrapperRef = useRef>(); - - useImperativeHandle( - ref, - () => ({ - /** - * Scrolls to the item at the specified `sectionIndex` and `itemIndex` (within the section) - * positioned in the viewable area such that `viewPosition` 0 places it at the top (and may be - * covered by a sticky header), 1 at the bottom, and 0.5 centered in the middle. `viewOffset` is a - * fixed number of pixels to offset the final target position, e.g. to compensate for sticky - * headers. - * - * Note: cannot scroll to locations outside the render window without specifying the - * `getItemLayout` prop. - */ - scrollToLocation(params: ScrollToLocationParamsType) { - wrapperRef.current?.scrollToLocation(params); - }, - - /** - * Tells the list an interaction has occurred, which should trigger viewability calculations, e.g. - * if `waitForInteractions` is true and the user has not scrolled. This is typically called by - * taps on items or by navigation actions. - */ - recordInteraction() { - wrapperRef.current?.getListRef()?.recordInteraction(); - }, - - /** - * Displays the scroll indicators momentarily. - * - * @platform ios - */ - flashScrollIndicators() { - wrapperRef.current?.getListRef()?.flashScrollIndicators(); - }, - - /** - * Provides a handle to the underlying scroll responder. - */ - getScrollResponder(): ?ScrollResponderType { - wrapperRef.current?.getListRef()?.getScrollResponder(); - }, - - getScrollableNode(): any { - wrapperRef.current?.getListRef()?.getScrollableNode(); - }, - - setNativeProps(nativeProps: Object) { - wrapperRef.current?.getListRef()?.setNativeProps(nativeProps); - }, - }), - [wrapperRef], - ); - - return ( - items.length} - getItem={(items, index) => items[index]} - /> - ); -}; - -export default SectionList; diff --git a/packages/react-native/Libraries/LogBox/UI/AnsiHighlight.js b/packages/react-native/Libraries/LogBox/UI/AnsiHighlight.js index 8871f67c6bb0..26f25b6b8578 100644 --- a/packages/react-native/Libraries/LogBox/UI/AnsiHighlight.js +++ b/packages/react-native/Libraries/LogBox/UI/AnsiHighlight.js @@ -4,7 +4,7 @@ * This source code is licensed under the MIT license found in the * LICENSE file in the root directory of this source tree. * - * @flow + * @flow strict-local * @format */ @@ -13,6 +13,7 @@ import type {TextStyleProp} from '../../StyleSheet/StyleSheet'; import View from '../../Components/View/View'; import StyleSheet from '../../StyleSheet/StyleSheet'; import Text from '../../Text/Text'; +// $FlowFixMe[untyped-import] import {ansiToJson} from 'anser'; import * as React from 'react'; diff --git a/packages/react-native/Libraries/Network/FormData.js b/packages/react-native/Libraries/Network/FormData.js index 3e7b02e669dc..551a1eca57f9 100644 --- a/packages/react-native/Libraries/Network/FormData.js +++ b/packages/react-native/Libraries/Network/FormData.js @@ -14,7 +14,7 @@ type FormDataValue = string | {name?: string, type?: string, uri: string}; type FormDataNameValuePair = [string, FormDataValue]; type Headers = {[name: string]: string, ...}; -type FormDataPart = +export type FormDataPart = | { string: string, headers: Headers, diff --git a/packages/react-native/Libraries/Network/RCTNetworking.android.js b/packages/react-native/Libraries/Network/RCTNetworking.android.js index c79b584a4d3c..972ad5162088 100644 --- a/packages/react-native/Libraries/Network/RCTNetworking.android.js +++ b/packages/react-native/Libraries/Network/RCTNetworking.android.js @@ -4,7 +4,7 @@ * This source code is licensed under the MIT license found in the * LICENSE file in the root directory of this source tree. * - * @flow + * @flow strict-local * @format */ @@ -21,10 +21,11 @@ import convertRequestBody from './convertRequestBody'; import NativeNetworkingAndroid from './NativeNetworkingAndroid'; type Header = [string, string]; +type HeadersMap = {[string]: string}; // Convert FormData headers to arrays, which are easier to consume in // native on Android. -function convertHeadersMapToArray(headers: Object): Array
{ +function convertHeadersMapToArray(headers: HeadersMap): Array
{ const headerArray: Array
= []; for (const name in headers) { headerArray.push([name, headers[name]]); @@ -37,7 +38,7 @@ function generateRequestId(): number { return _requestId++; } -const emitter = new NativeEventEmitter<$FlowFixMe>( +const emitter = new NativeEventEmitter( // T88715063: NativeEventEmitter only used this parameter on iOS. Now it uses it on all platforms, so this code was modified automatically to preserve its behavior // If you want to use the native module on other platforms, please remove this condition and test its behavior Platform.OS !== 'ios' ? null : NativeNetworkingAndroid, @@ -53,7 +54,6 @@ const RCTNetworking = { listener: (...RCTNetworkingEventDefinitions[K]) => unknown, context?: unknown, ): EventSubscription { - // $FlowFixMe[incompatible-type] return emitter.addListener(eventType, listener, context); }, @@ -61,8 +61,8 @@ const RCTNetworking = { method: string, trackingName: string | void, url: string, - headers: Object, - data: RequestBody, + headers: HeadersMap, + data: ?RequestBody, responseType: NativeResponseType, incrementalUpdates: boolean, timeout: number, @@ -70,12 +70,17 @@ const RCTNetworking = { withCredentials: boolean, ) { const body = convertRequestBody(data); - if (body && body.formData) { - body.formData = body.formData.map(part => ({ - ...part, - headers: convertHeadersMapToArray(part.headers), - })); - } + const formData = body?.formData; + const nativeRequestBody = + formData != null + ? { + ...body, + formData: formData.map(part => ({ + ...part, + headers: convertHeadersMapToArray(part.headers), + })), + } + : body; const requestId = generateRequestId(); const devToolsRequestId = global.__NETWORK_REPORTER__?.createDevToolsRequestId(); @@ -84,7 +89,7 @@ const RCTNetworking = { url, requestId, convertHeadersMapToArray(headers), - {...body, trackingName, devToolsRequestId}, + {...nativeRequestBody, trackingName, devToolsRequestId}, responseType, incrementalUpdates, timeout, diff --git a/packages/react-native/Libraries/Network/RCTNetworking.ios.js b/packages/react-native/Libraries/Network/RCTNetworking.ios.js index a97c96dec1f9..79bb13c8e2c2 100644 --- a/packages/react-native/Libraries/Network/RCTNetworking.ios.js +++ b/packages/react-native/Libraries/Network/RCTNetworking.ios.js @@ -32,7 +32,7 @@ const RCTNetworking = { trackingName: string | void, url: string, headers: {...}, - data: RequestBody, + data: ?RequestBody, responseType: NativeResponseType, incrementalUpdates: boolean, timeout: number, diff --git a/packages/react-native/Libraries/Network/RCTNetworking.js.flow b/packages/react-native/Libraries/Network/RCTNetworking.js.flow index c1802972b4ea..d3dccfbe5006 100644 --- a/packages/react-native/Libraries/Network/RCTNetworking.js.flow +++ b/packages/react-native/Libraries/Network/RCTNetworking.js.flow @@ -27,8 +27,8 @@ declare const RCTNetworking: interface { method: string, trackingName: string | void, url: string, - headers: {...}, - data: RequestBody, + headers: {[string]: string}, + data: ?RequestBody, responseType: NativeResponseType, incrementalUpdates: boolean, timeout: number, diff --git a/packages/react-native/Libraries/Network/XMLHttpRequest.js b/packages/react-native/Libraries/Network/XMLHttpRequest.js index 3affe5a0e199..178926bd886e 100644 --- a/packages/react-native/Libraries/Network/XMLHttpRequest.js +++ b/packages/react-native/Libraries/Network/XMLHttpRequest.js @@ -4,16 +4,20 @@ * This source code is licensed under the MIT license found in the * LICENSE file in the root directory of this source tree. * - * @flow + * @flow strict-local * @format */ +// flowlint unsafe-getters-setters:off + 'use strict'; import type { EventCallback, EventListener, } from '../../src/private/webapis/dom/events/EventTarget'; +import type Blob from '../Blob/Blob'; +import type {RequestBody} from './convertRequestBody'; import Event from '../../src/private/webapis/dom/events/Event'; import { @@ -30,20 +34,34 @@ const RCTNetworking = require('./RCTNetworking').default; const base64 = require('base64-js'); const invariant = require('invariant'); -const DEBUG_NETWORK_SEND_DELAY: false = false; // Set to a number of milliseconds when debugging +const DEBUG_NETWORK_SEND_DELAY: number = 0; // Set to a number of milliseconds when debugging export type NativeResponseType = 'base64' | 'blob' | 'text'; export type ResponseType = '' | 'arraybuffer' | 'blob' | 'document' | 'json' | 'text'; -export type Response = ?Object | string; +export type Response = + | null + | void + | string + | number + | boolean + | ArrayBufferLike + | Blob + | ReadonlyArray + | {[string]: unknown, ...}; type XHRInterceptor = interface { - requestSent(id: number, url: string, method: string, headers: Object): void, + requestSent( + id: number, + url: string, + method: string, + headers: {[string]: string}, + ): void, responseReceived( id: number, url: string, status: number, - headers: Object, + headers: {[string]: string}, ): void, dataReceived(id: number, data: string): void, loadingFinished(id: number, encodedDataLength: number): void, @@ -145,7 +163,7 @@ class XMLHttpRequest extends EventTarget { DONE: number = DONE; readyState: number = UNSENT; - responseHeaders: ?Object; + responseHeaders: ?{[string]: string}; status: number = 0; timeout: number = 0; responseURL: ?string; @@ -159,8 +177,8 @@ class XMLHttpRequest extends EventTarget { _aborted: boolean = false; _cachedResponse: Response; _hasError: boolean = false; - _headers: Object; - _lowerCaseResponseHeaders: Object; + _headers: {[string]: string}; + _lowerCaseResponseHeaders: {[string]: string}; _method: ?string = null; _perfKey: ?string = null; _responseType: ResponseType; @@ -302,13 +320,14 @@ class XMLHttpRequest extends EventTarget { __didCreateRequest(requestId: number): void { this._requestId = requestId; - XMLHttpRequest._interceptor && + if (XMLHttpRequest._interceptor != null) { XMLHttpRequest._interceptor.requestSent( requestId, - this._url || '', - this._method || 'GET', + this._url ?? '', + this._method ?? 'GET', this._headers, ); + } } // exposed for testing @@ -332,7 +351,7 @@ class XMLHttpRequest extends EventTarget { __didReceiveResponse( requestId: number, status: number, - responseHeaders: ?Object, + responseHeaders: ?{[string]: string}, responseURL: ?string, ): void { if (requestId === this._requestId) { @@ -343,19 +362,20 @@ class XMLHttpRequest extends EventTarget { this.status = status; this.setResponseHeaders(responseHeaders); this.setReadyState(this.HEADERS_RECEIVED); - if (responseURL || responseURL === '') { + if (responseURL != null) { this.responseURL = responseURL; } else { delete this.responseURL; } - XMLHttpRequest._interceptor && + if (XMLHttpRequest._interceptor != null) { XMLHttpRequest._interceptor.responseReceived( requestId, - responseURL || this._url || '', + responseURL ?? this._url ?? '', status, - responseHeaders || {}, + responseHeaders ?? {}, ); + } } } @@ -509,7 +529,7 @@ class XMLHttpRequest extends EventTarget { return value !== undefined ? value : null; } - setRequestHeader(header: string, value: any): void { + setRequestHeader(header: string, value: string): void { if (this.readyState !== this.OPENED) { throw new Error('Request has not been opened'); } @@ -546,7 +566,7 @@ class XMLHttpRequest extends EventTarget { if (this.readyState !== this.UNSENT) { throw new Error('Cannot open, already sending'); } - if (async !== undefined && !async) { + if (async !== undefined && !Boolean(async)) { // async is default throw new Error('Synchronous http requests are not supported'); } @@ -559,7 +579,7 @@ class XMLHttpRequest extends EventTarget { this.setReadyState(this.OPENED); } - send(data: any): void { + send(data: ?RequestBody): void { if (this.readyState !== this.OPENED) { throw new Error('Request has not been opened'); } @@ -616,33 +636,32 @@ class XMLHttpRequest extends EventTarget { this._perfKey = 'network_XMLHttpRequest_' + String(friendlyName); performanceLogger.startTimespan(this._perfKey); } + const method = this._method; invariant( - this._method, + method != null && method !== '', 'XMLHttpRequest method needs to be defined (%s).', friendlyName, ); + const url = this._url; invariant( - this._url, + url != null && url !== '', 'XMLHttpRequest URL needs to be defined (%s).', friendlyName, ); RCTNetworking.sendRequest( - this._method, + method, this._trackingName ?? undefined, - this._url, + url, this._headers, data, nativeResponseType, incrementalEvents, this.timeout, - // $FlowFixMe[method-unbinding] added when improving typing for this parameters - this.__didCreateRequest.bind(this), + (requestId: number) => this.__didCreateRequest(requestId), this.withCredentials, ); }; - /* $FlowFixMe[constant-condition] Error discovered during Constant - * Condition roll out. See https://fburl.com/workplace/1v97vimq. */ - if (DEBUG_NETWORK_SEND_DELAY) { + if (DEBUG_NETWORK_SEND_DELAY > 0) { setTimeout(doSend, DEBUG_NETWORK_SEND_DELAY); } else { doSend(); @@ -651,7 +670,7 @@ class XMLHttpRequest extends EventTarget { abort(): void { this._aborted = true; - if (this._requestId) { + if (this._requestId != null) { RCTNetworking.abortRequest(this._requestId); } // only call onreadystatechange if there is something to abort, @@ -668,13 +687,12 @@ class XMLHttpRequest extends EventTarget { this._reset(); } - setResponseHeaders(responseHeaders: ?Object): void { + setResponseHeaders(responseHeaders: ?{[string]: string}): void { this.responseHeaders = responseHeaders || null; - const headers = responseHeaders || {}; + const headers: {[string]: string} = responseHeaders || {}; this._lowerCaseResponseHeaders = Object.keys(headers).reduce<{ - [string]: any, + [string]: string, }>((lcaseHeaders, headerName) => { - // $FlowFixMe[invalid-computed-prop] lcaseHeaders[headerName.toLowerCase()] = headers[headerName]; return lcaseHeaders; }, {}); diff --git a/packages/react-native/Libraries/Network/convertRequestBody.js b/packages/react-native/Libraries/Network/convertRequestBody.js index caf79174078d..c1c8f8b64269 100644 --- a/packages/react-native/Libraries/Network/convertRequestBody.js +++ b/packages/react-native/Libraries/Network/convertRequestBody.js @@ -4,28 +4,62 @@ * This source code is licensed under the MIT license found in the * LICENSE file in the root directory of this source tree. * - * @flow + * @flow strict-local * @format */ 'use strict'; import typeof BlobT from '../Blob/Blob'; +import type {BlobData} from '../Blob/BlobTypes'; +import type {FormDataPart} from './FormData'; import typeof FormDataT from './FormData'; const Blob: BlobT = require('../Blob/Blob').default; const binaryToBase64 = require('../Utilities/binaryToBase64').default; const FormData: FormDataT = require('./FormData').default; +type URIRequestBody = Readonly<{ + uri: string, + string?: string, + blob?: BlobData, + formData?: Array, + base64?: string, + ... +}>; + export type RequestBody = - | string - | Blob - | FormData - | {uri: string, ...} - | ArrayBuffer - | $ArrayBufferView; - -function convertRequestBody(body: RequestBody): Object { + string | Blob | FormData | URIRequestBody | ArrayBuffer | $ArrayBufferView; + +type RequestBodyResult = Readonly<{ + string?: string, + blob?: BlobData, + formData?: Array, + base64?: string, + uri?: string, + ... +}>; + +declare function isArrayBufferView( + body: unknown, +): implies body is $ArrayBufferView; +function isArrayBufferView(body: unknown) { + return ArrayBuffer.isView(body); +} + +declare function isURIRequestBody( + body: unknown, +): implies body is URIRequestBody; +function isURIRequestBody(body: unknown) { + return ( + body != null && + typeof body === 'object' && + 'uri' in body && + typeof body.uri === 'string' + ); +} + +function convertRequestBody(body: ?RequestBody): ?RequestBodyResult { if (typeof body === 'string') { return {string: body}; } @@ -35,12 +69,13 @@ function convertRequestBody(body: RequestBody): Object { if (body instanceof FormData) { return {formData: body.getParts()}; } - if (body instanceof ArrayBuffer || ArrayBuffer.isView(body)) { - /* $FlowFixMe[incompatible-type] : no way to assert that 'body' is indeed - * an ArrayBufferView */ + if (body instanceof ArrayBuffer || isArrayBufferView(body)) { return {base64: binaryToBase64(body)}; } - return body; + if (isURIRequestBody(body)) { + return body; + } + return null; } export default convertRequestBody; diff --git a/packages/react-native/Libraries/PushNotificationIOS/PushNotificationIOS.js b/packages/react-native/Libraries/PushNotificationIOS/PushNotificationIOS.js index a8a6774614bd..7de529ce4f8b 100644 --- a/packages/react-native/Libraries/PushNotificationIOS/PushNotificationIOS.js +++ b/packages/react-native/Libraries/PushNotificationIOS/PushNotificationIOS.js @@ -4,7 +4,7 @@ * This source code is licensed under the MIT license found in the * LICENSE file in the root directory of this source tree. * - * @flow + * @flow strict-local * @format */ @@ -28,6 +28,7 @@ type PresentLocalNotificationDetails = { alertTitle?: string, soundName?: string, category?: string, + // $FlowFixMe[unclear-type] userInfo?: Object, applicationIconBadgeNumber?: number, fireDate?: number, @@ -122,6 +123,7 @@ export interface PushNotification { /** * An alias for `getAlert` to get the notification's main message string */ + // $FlowFixMe[unclear-type] getMessage(): ?string | ?Object; /** @@ -137,6 +139,7 @@ export interface PushNotification { /** * Gets the notification's main message from the `aps` object */ + // $FlowFixMe[unclear-type] getAlert(): ?string | ?Object; /** @@ -152,6 +155,7 @@ export interface PushNotification { /** * Gets the data object on the notif */ + // $FlowFixMe[unclear-type] getData(): ?Object; /** @@ -174,7 +178,9 @@ export interface PushNotification { * @deprecated Use [@react-native-community/push-notification-ios](https://www.npmjs.com/package/@react-native-community/push-notification-ios) instead */ class PushNotificationIOS { + // $FlowFixMe[unclear-type] _data: Object; + // $FlowFixMe[unclear-type] _alert: string | Object; _sound: string; _category: string; @@ -276,6 +282,7 @@ class PushNotificationIOS { * See https://reactnative.dev/docs/pushnotificationios#getdeliverednotifications */ static getDeliveredNotifications( + // $FlowFixMe[unclear-type] callback: (notifications: Array) => void, ): void { invariant( @@ -316,6 +323,7 @@ class PushNotificationIOS { * * See https://reactnative.dev/docs/pushnotificationios#getapplicationiconbadgenumber */ + // $FlowFixMe[unclear-type] static getApplicationIconBadgeNumber(callback: Function): void { invariant( NativePushNotificationManagerIOS, @@ -330,6 +338,7 @@ class PushNotificationIOS { * * See https://reactnative.dev/docs/pushnotificationios#cancellocalnotification */ + // $FlowFixMe[unclear-type] static cancelLocalNotifications(userInfo: Object): void { invariant( NativePushNotificationManagerIOS, @@ -343,6 +352,7 @@ class PushNotificationIOS { * * See https://reactnative.dev/docs/pushnotificationios#getscheduledlocalnotifications */ + // $FlowFixMe[unclear-type] static getScheduledLocalNotifications(callback: Function): void { invariant( NativePushNotificationManagerIOS, @@ -359,6 +369,7 @@ class PushNotificationIOS { */ static addEventListener( type: PushNotificationEventName, + // $FlowFixMe[unclear-type] handler: Function, ): void { invariant( @@ -539,6 +550,7 @@ class PushNotificationIOS { * `getInitialNotification` is sufficient. * */ + // $FlowFixMe[unclear-type] constructor(nativeNotif: Object) { this._data = {}; this._remoteNotificationCompleteCallbackCalled = false; @@ -603,6 +615,7 @@ class PushNotificationIOS { /** * An alias for `getAlert` to get the notification's main message string. */ + // $FlowFixMe[unclear-type] getMessage(): ?string | ?Object { // alias because "alert" is an ambiguous name return this._alert; @@ -633,6 +646,7 @@ class PushNotificationIOS { * * See https://reactnative.dev/docs/pushnotificationios#getalert */ + // $FlowFixMe[unclear-type] getAlert(): ?string | ?Object { return this._alert; } @@ -660,6 +674,7 @@ class PushNotificationIOS { * * See https://reactnative.dev/docs/pushnotificationios#getdata */ + // $FlowFixMe[unclear-type] getData(): ?Object { return this._data; } diff --git a/packages/react-native/Libraries/ReactNative/AppContainer.js b/packages/react-native/Libraries/ReactNative/AppContainer.js index a9e6fff6541e..9c26edfd1e65 100644 --- a/packages/react-native/Libraries/ReactNative/AppContainer.js +++ b/packages/react-native/Libraries/ReactNative/AppContainer.js @@ -4,7 +4,7 @@ * This source code is licensed under the MIT license found in the * LICENSE file in the root directory of this source tree. * - * @flow + * @flow strict-local * @format */ @@ -17,6 +17,7 @@ export type Props = Readonly<{ children?: React.Node, rootTag: number | RootTag, initialProps?: {...}, + // $FlowFixMe[unclear-type] WrapperComponent?: ?React.ComponentType, rootViewStyle?: ?ViewStyleProp, internal_excludeLogBox?: boolean, diff --git a/packages/react-native/Libraries/ReactNative/AppRegistry.flow.js b/packages/react-native/Libraries/ReactNative/AppRegistry.flow.js index 94de6edde6ec..8bb995c4e242 100644 --- a/packages/react-native/Libraries/ReactNative/AppRegistry.flow.js +++ b/packages/react-native/Libraries/ReactNative/AppRegistry.flow.js @@ -4,7 +4,7 @@ * This source code is licensed under the MIT license found in the * LICENSE file in the root directory of this source tree. * - * @flow + * @flow strict-local * @format */ @@ -13,13 +13,16 @@ import type {RootTag} from '../Types/RootTagTypes'; import type {DisplayModeType} from './DisplayMode'; import type {IPerformanceLogger} from './IPerformanceLogger.flow'; +// $FlowFixMe[unclear-type] type HeadlessTask = (taskData: any) => Promise; export type TaskProvider = () => HeadlessTask; +// $FlowFixMe[unclear-type] export type ComponentProvider = () => React.ComponentType; export type ComponentProviderInstrumentationHook = ( component_: ComponentProvider, scopedPerformanceLogger: IPerformanceLogger, + // $FlowFixMe[unclear-type] ) => React.ComponentType; export type AppConfig = { appKey: string, @@ -43,7 +46,10 @@ export type Registry = { ... }; export type WrapperComponentProvider = ( - appParameters: Object, + appParameters?: AppParameters, appKey?: string, + // $FlowFixMe[unclear-type] ) => React.ComponentType; -export type RootViewStyleProvider = (appParameters: Object) => ViewStyleProp; +export type RootViewStyleProvider = ( + appParameters: AppParameters, +) => ViewStyleProp; diff --git a/packages/react-native/Libraries/ReactNative/BridgelessUIManager.js b/packages/react-native/Libraries/ReactNative/BridgelessUIManager.js index 0db6d3415496..9ac0838f2566 100644 --- a/packages/react-native/Libraries/ReactNative/BridgelessUIManager.js +++ b/packages/react-native/Libraries/ReactNative/BridgelessUIManager.js @@ -4,7 +4,7 @@ * This source code is licensed under the MIT license found in the * LICENSE file in the root directory of this source tree. * - * @flow + * @flow strict-local * @format */ @@ -12,6 +12,7 @@ import type {RootTag} from '../Types/RootTagTypes'; import type {UIManagerJSInterface} from '../Types/UIManagerJSInterface'; +import type {UIManagerConstants, ViewManagerConfig} from './NativeUIManager'; import {unstable_hasComponent} from '../NativeComponent/NativeComponentRegistryUnstable'; import defineLazyObjectProperty from '../Utilities/defineLazyObjectProperty'; @@ -19,20 +20,54 @@ import Platform from '../Utilities/Platform'; import {getFabricUIManager} from './FabricUIManager'; import nullthrows from 'nullthrows'; +function getNativeTagFromInternalInstanceHandle( + internalInstanceHandle: unknown, +): ?number { + if ( + internalInstanceHandle == null || + typeof internalInstanceHandle !== 'object' || + !('stateNode' in internalInstanceHandle) + ) { + return null; + } + + const {stateNode} = internalInstanceHandle; + if ( + stateNode == null || + typeof stateNode !== 'object' || + !('canonical' in stateNode) + ) { + return null; + } + + const {canonical} = stateNode; + if ( + canonical == null || + typeof canonical !== 'object' || + !('nativeTag' in canonical) + ) { + return null; + } + + const {nativeTag} = canonical; + return typeof nativeTag === 'number' ? nativeTag : null; +} + function raiseSoftError(methodName: string, details?: string): void { console.error( `[ReactNative Architecture][JS] '${methodName}' is not available in the new React Native architecture.` + - (details ? ` ${details}` : ''), + (details != null && details !== '' ? ` ${details}` : ''), ); } -const getUIManagerConstants: ?() => {[viewManagerName: string]: Object} = - global.RN$LegacyInterop_UIManager_getConstants; +const getUIManagerConstants: ?() => { + [viewManagerName: string]: ViewManagerConfig, +} = global.RN$LegacyInterop_UIManager_getConstants; const getUIManagerConstantsCached = (function () { let wasCalledOnce = false; - let result: {[viewManagerName: string]: Object} = {}; - return (): {[viewManagerName: string]: Object} => { + let result: {[viewManagerName: string]: ViewManagerConfig} = {}; + return (): {[viewManagerName: string]: ViewManagerConfig} => { if (!wasCalledOnce) { result = nullthrows(getUIManagerConstants)(); wasCalledOnce = true; @@ -41,16 +76,18 @@ const getUIManagerConstantsCached = (function () { }; })(); -const getConstantsForViewManager: ?(viewManagerName: string) => ?Object = +const getConstantsForViewManager: ?( + viewManagerName: string, +) => ?ViewManagerConfig = global.RN$LegacyInterop_UIManager_getConstantsForViewManager; -const getDefaultEventTypes: ?() => Object = +const getDefaultEventTypes: ?() => ViewManagerConfig = global.RN$LegacyInterop_UIManager_getDefaultEventTypes; const getDefaultEventTypesCached = (function () { let wasCalledOnce = false; let result = null; - return (): Object => { + return (): ViewManagerConfig => { if (!wasCalledOnce) { result = nullthrows(getDefaultEventTypes)(); wasCalledOnce = true; @@ -63,7 +100,13 @@ const getDefaultEventTypesCached = (function () { * UIManager.js overrides these APIs. * Pull them out from the BridgelessUIManager implementation. So, we can ignore them. */ -const UIManagerJSOverridenAPIs = { +const UIManagerJSOverridenAPIs: { + measure: UIManagerJSInterface['measure'], + measureInWindow: UIManagerJSInterface['measureInWindow'], + measureLayout: UIManagerJSInterface['measureLayout'], + measureLayoutRelativeToParent: UIManagerJSInterface['measureLayoutRelativeToParent'], + dispatchViewManagerCommand: UIManagerJSInterface['dispatchViewManagerCommand'], +} = { measure: ( reactTag: number, callback: ( @@ -86,7 +129,7 @@ const UIManagerJSOverridenAPIs = { measureLayout: ( reactTag: number, ancestorReactTag: number, - errorCallback: (error: Object) => void, + errorCallback, callback: ( left: number, top: number, @@ -98,7 +141,7 @@ const UIManagerJSOverridenAPIs = { }, measureLayoutRelativeToParent: ( reactTag: number, - errorCallback: (error: Object) => void, + errorCallback, callback: ( left: number, top: number, @@ -111,7 +154,7 @@ const UIManagerJSOverridenAPIs = { dispatchViewManagerCommand: ( reactTag: number, commandID: number, - commandArgs: ?Array, + commandArgs, ): void => { raiseSoftError('dispatchViewManagerCommand'); }, @@ -122,16 +165,23 @@ const UIManagerJSOverridenAPIs = { * In OSS, the New Architecture will just use the Fabric renderer, which uses * different APIs. */ -const UIManagerJSUnusedInNewArchAPIs = { +const UIManagerJSUnusedInNewArchAPIs: { + createView: UIManagerJSInterface['createView'], + updateView: UIManagerJSInterface['updateView'], + setChildren: UIManagerJSInterface['setChildren'], + manageChildren: UIManagerJSInterface['manageChildren'], + setJSResponder: UIManagerJSInterface['setJSResponder'], + clearJSResponder: UIManagerJSInterface['clearJSResponder'], +} = { createView: ( reactTag: number, viewName: string, rootTag: RootTag, - props: Object, + props, ): void => { raiseSoftError('createView'); }, - updateView: (reactTag: number, viewName: string, props: Object): void => { + updateView: (reactTag: number, viewName: string, props): void => { raiseSoftError('updateView'); }, setChildren: (containerTag: number, reactTags: Array): void => { @@ -165,7 +215,9 @@ const UIManagerJSDeprecatedPlatformAPIs = Platform.select({ const UIManagerJSPlatformAPIs = Platform.select({ android: { - getConstantsForViewManager: (viewManagerName: string): ?Object => { + getConstantsForViewManager: ( + viewManagerName: string, + ): ?ViewManagerConfig => { if (getConstantsForViewManager) { return getConstantsForViewManager(viewManagerName); } @@ -173,13 +225,13 @@ const UIManagerJSPlatformAPIs = Platform.select({ raiseSoftError('getConstantsForViewManager'); return {}; }, - getDefaultEventTypes: (): Array => { + getDefaultEventTypes: (): ViewManagerConfig => { if (getDefaultEventTypes) { return getDefaultEventTypesCached(); } raiseSoftError('getDefaultEventTypes'); - return []; + return {}; }, setLayoutAnimationEnabledExperimental: (enabled: boolean): void => { if (__DEV__) { @@ -217,7 +269,7 @@ const UIManagerJSPlatformAPIs = Platform.select({ const FabricUIManager = nullthrows(getFabricUIManager()); const shadowNode = FabricUIManager.findShadowNodeByTag_DEPRECATED(reactTag); - if (!shadowNode) { + if (shadowNode == null) { console.error( `sendAccessibilityEvent() dropping event: Cannot find view with tag #${reactTag}`, ); @@ -233,7 +285,7 @@ const UIManagerJSPlatformAPIs = Platform.select({ * * Leave this unimplemented until we implement lazy loading of legacy modules and view managers in the new architecture. */ - lazilyLoadView: (name: string): Object => { + lazilyLoadView: (name: string): ViewManagerConfig => { raiseSoftError('lazilyLoadView'); return {}; }, @@ -241,7 +293,7 @@ const UIManagerJSPlatformAPIs = Platform.select({ const FabricUIManager = nullthrows(getFabricUIManager()); const shadowNode = FabricUIManager.findShadowNodeByTag_DEPRECATED(reactTag); - if (!shadowNode) { + if (shadowNode == null) { console.error(`focus() noop: Cannot find view with tag #${reactTag}`); return; } @@ -251,7 +303,7 @@ const UIManagerJSPlatformAPIs = Platform.select({ const FabricUIManager = nullthrows(getFabricUIManager()); const shadowNode = FabricUIManager.findShadowNodeByTag_DEPRECATED(reactTag); - if (!shadowNode) { + if (shadowNode == null) { console.error(`blur() noop: Cannot find view with tag #${reactTag}`); return; } @@ -260,12 +312,12 @@ const UIManagerJSPlatformAPIs = Platform.select({ }, }); -const UIManagerJS: UIManagerJSInterface & {[string]: any} = { +const UIManagerJS: UIManagerJSInterface & {[string]: ViewManagerConfig} = { ...UIManagerJSOverridenAPIs, ...UIManagerJSDeprecatedPlatformAPIs, ...UIManagerJSPlatformAPIs, ...UIManagerJSUnusedInNewArchAPIs, - getViewManagerConfig: (viewManagerName: string): unknown => { + getViewManagerConfig: (viewManagerName: string): ViewManagerConfig => { if (getUIManagerConstants) { const constants = getUIManagerConstantsCached(); if ( @@ -287,7 +339,7 @@ const UIManagerJS: UIManagerJSInterface & {[string]: any} = { hasViewManagerConfig: (viewManagerName: string): boolean => { return unstable_hasComponent(viewManagerName); }, - getConstants: (): Object => { + getConstants: (): UIManagerConstants => { if (getUIManagerConstants) { return getUIManagerConstantsCached(); } else { @@ -309,7 +361,7 @@ const UIManagerJS: UIManagerJSInterface & {[string]: any} = { const FabricUIManager = nullthrows(getFabricUIManager()); const shadowNode = FabricUIManager.findShadowNodeByTag_DEPRECATED(reactTag); - if (!shadowNode) { + if (shadowNode == null) { console.error( `findSubviewIn() noop: Cannot find view with reactTag ${reactTag}`, ); @@ -326,16 +378,26 @@ const UIManagerJS: UIManagerJSInterface & {[string]: any} = { return; } - let instanceHandle: Object = internalInstanceHandle; - let node = instanceHandle.stateNode.node; - - if (!node) { - console.error('findSubviewIn(): Cannot find node at point'); + const { + getNodeFromInternalInstanceHandle, + } = require('./RendererImplementation'); + const node = getNodeFromInternalInstanceHandle(internalInstanceHandle); + if (node == null) { + console.error( + 'findSubviewIn(): Cannot resolve the node at the requested point', + ); return; } - let nativeViewTag: number = - instanceHandle.stateNode.canonical.nativeTag; + const nativeViewTag = getNativeTagFromInternalInstanceHandle( + internalInstanceHandle, + ); + if (nativeViewTag == null) { + console.error( + 'findSubviewIn(): Cannot resolve the native tag at the requested point', + ); + return; + } FabricUIManager.measure( node, @@ -353,7 +415,7 @@ const UIManagerJS: UIManagerJSInterface & {[string]: any} = { ): void => { const FabricUIManager = nullthrows(getFabricUIManager()); const shadowNode = FabricUIManager.findShadowNodeByTag_DEPRECATED(reactTag); - if (!shadowNode) { + if (shadowNode == null) { console.error( `viewIsDescendantOf() noop: Cannot find view with reactTag ${reactTag}`, ); @@ -362,7 +424,7 @@ const UIManagerJS: UIManagerJSInterface & {[string]: any} = { const ancestorShadowNode = FabricUIManager.findShadowNodeByTag_DEPRECATED(ancestorReactTag); - if (!ancestorShadowNode) { + if (ancestorShadowNode == null) { console.error( `viewIsDescendantOf() noop: Cannot find view with ancestorReactTag ${ancestorReactTag}`, ); @@ -382,11 +444,7 @@ const UIManagerJS: UIManagerJSInterface & {[string]: any} = { callback([isAncestor]); }, - configureNextLayoutAnimation: ( - config: Object, - callback: () => void, - errorCallback: (error: Object) => void, - ): void => { + configureNextLayoutAnimation: (config, callback, errorCallback): void => { const FabricUIManager = nullthrows(getFabricUIManager()); FabricUIManager.configureNextLayoutAnimation( config, diff --git a/packages/react-native/Libraries/ReactNative/PaperUIManager.js b/packages/react-native/Libraries/ReactNative/PaperUIManager.js index fadf460022fd..151b06fe457c 100644 --- a/packages/react-native/Libraries/ReactNative/PaperUIManager.js +++ b/packages/react-native/Libraries/ReactNative/PaperUIManager.js @@ -4,12 +4,13 @@ * This source code is licensed under the MIT license found in the * LICENSE file in the root directory of this source tree. * - * @flow + * @flow strict-local * @format */ import type {RootTag} from '../Types/RootTagTypes'; import type {UIManagerJSInterface} from '../Types/UIManagerJSInterface'; +import type {UIManagerConstants, ViewManagerConfig} from './NativeUIManager'; import NativeUIManager from './NativeUIManager'; import nullthrows from 'nullthrows'; @@ -20,13 +21,13 @@ const defineLazyObjectProperty = const Platform = require('../Utilities/Platform').default; const UIManagerProperties = require('./UIManagerProperties').default; -const viewManagerConfigs: {[string]: any | null} = {}; +const viewManagerConfigs: {[string]: ViewManagerConfig | null} = {}; const triedLoadingConfig = new Set(); let NativeUIManagerConstants = {}; let isNativeUIManagerConstantsSet = false; -function getConstants(): Object { +function getConstants(): UIManagerConstants { if (!isNativeUIManagerConstantsSet) { NativeUIManagerConstants = NativeUIManager.getConstants(); isNativeUIManagerConstantsSet = true; @@ -34,7 +35,7 @@ function getConstants(): Object { return NativeUIManagerConstants; } -function getViewManagerConfig(viewManagerName: string): any { +function getViewManagerConfig(viewManagerName: string): ViewManagerConfig { if ( viewManagerConfigs[viewManagerName] === undefined && NativeUIManager.getConstantsForViewManager @@ -86,7 +87,7 @@ const UIManagerJS: UIManagerJSInterface = { reactTag: number, viewName: string, rootTag: RootTag, - props: Object, + props, ): void { if (Platform.OS === 'ios' && viewManagerConfigs[viewName] === undefined) { // This is necessary to force the initialization of native viewManager @@ -96,10 +97,10 @@ const UIManagerJS: UIManagerJSInterface = { NativeUIManager.createView(reactTag, viewName, rootTag, props); }, - getConstants(): Object { + getConstants(): UIManagerConstants { return getConstants(); }, - getViewManagerConfig(viewManagerName: string): any { + getViewManagerConfig(viewManagerName: string): ViewManagerConfig { return getViewManagerConfig(viewManagerName); }, hasViewManagerConfig(viewManagerName: string): boolean { diff --git a/packages/react-native/Libraries/ReactNative/ReactFabricPublicInstance/ReactNativeAttributePayload.js b/packages/react-native/Libraries/ReactNative/ReactFabricPublicInstance/ReactNativeAttributePayload.js index 0f1edf031d0f..7d8f1a275fc6 100644 --- a/packages/react-native/Libraries/ReactNative/ReactFabricPublicInstance/ReactNativeAttributePayload.js +++ b/packages/react-native/Libraries/ReactNative/ReactFabricPublicInstance/ReactNativeAttributePayload.js @@ -4,7 +4,7 @@ * This source code is licensed under the MIT license found in the * LICENSE file in the root directory of this source tree. * - * @flow + * @flow strict-local * @format */ @@ -24,7 +24,7 @@ const emptyObject = {}; * across modules, I've kept them isolated to this module. */ -type NestedNode = Array | Object; +type NestedNode = Array | {[string]: unknown}; // Tracks removed keys let removedKeys: {[string]: boolean} | null = null; @@ -45,7 +45,7 @@ function defaultDiffer(prevProp: unknown, nextProp: unknown): boolean { } function restoreDeletedValuesInNestedArray( - updatePayload: Object, + updatePayload: {[string]: unknown}, node: NestedNode, validAttributes: AttributeConfiguration, ) { @@ -76,11 +76,9 @@ function restoreDeletedValuesInNestedArray( } if (typeof nextProp === 'function') { - // $FlowFixMe[incompatible-type] found when upgrading Flow nextProp = true; } if (typeof nextProp === 'undefined') { - // $FlowFixMe[incompatible-type] found when upgrading Flow nextProp = null; } @@ -106,11 +104,12 @@ function restoreDeletedValuesInNestedArray( } function diffNestedArrayProperty( - updatePayload: null | Object, + updatePayloadInput: null | {[string]: unknown}, prevArray: Array, nextArray: Array, validAttributes: AttributeConfiguration, -): null | Object { +): null | {[string]: unknown} { + let updatePayload = updatePayloadInput; const minLength = prevArray.length < nextArray.length ? prevArray.length : nextArray.length; let i; @@ -144,11 +143,11 @@ function diffNestedArrayProperty( } function diffNestedProperty( - updatePayload: null | Object, + updatePayload: null | {[string]: unknown}, prevProp: NestedNode, nextProp: NestedNode, validAttributes: AttributeConfiguration, -): null | Object { +): null | {[string]: unknown} { if (!updatePayload && prevProp === nextProp) { // If no properties have been added, then we can bail out quickly on object // equality. @@ -183,7 +182,9 @@ function diffNestedProperty( if (Array.isArray(prevProp)) { return diffProperties( updatePayload, + // $FlowFixMe[incompatible-type] flattenStyle is reused to flatten a nested style array here flattenStyle(prevProp), + // $FlowFixMe[incompatible-type] a non-array nested node is a plain props object here nextProp, validAttributes, ); @@ -192,6 +193,7 @@ function diffNestedProperty( return diffProperties( updatePayload, prevProp, + // $FlowFixMe[incompatible-type] flattenStyle is reused to flatten a nested style array here flattenStyle(nextProp), validAttributes, ); @@ -202,10 +204,11 @@ function diffNestedProperty( * adds a null sentinel to the updatePayload, for each prop key. */ function clearNestedProperty( - updatePayload: null | Object, + updatePayloadInput: null | {[string]: unknown}, prevProp: NestedNode, validAttributes: AttributeConfiguration, -): null | Object { +): null | {[string]: unknown} { + let updatePayload = updatePayloadInput; if (!prevProp) { return updatePayload; } @@ -233,14 +236,15 @@ function clearNestedProperty( * anything changed. */ function diffProperties( - updatePayload: null | Object, - prevProps: Object, - nextProps: Object, + updatePayloadInput: null | {[string]: unknown}, + prevProps: {[string]: unknown}, + nextProps: {[string]: unknown}, validAttributes: AttributeConfiguration, -): null | Object { +): null | {[string]: unknown} { + let updatePayload = updatePayloadInput; let attributeConfig; - let nextProp; - let prevProp; + let nextProp: unknown; + let prevProp: unknown; for (const propKey in nextProps) { attributeConfig = validAttributes[propKey]; @@ -258,11 +262,11 @@ function diffProperties( if (!attributeConfigHasProcess) { // functions are converted to booleans as markers that the associated // events should be sent from native. - nextProp = true as any; + nextProp = true; // If nextProp is not a function, then don't bother changing prevProp // since nextProp will win and go into the updatePayload regardless. if (typeof prevProp === 'function') { - prevProp = true as any; + prevProp = true; } } } @@ -270,9 +274,9 @@ function diffProperties( // An explicit value of undefined is treated as a null because it overrides // any other preceding value. if (typeof nextProp === 'undefined') { - nextProp = null as any; + nextProp = null; if (typeof prevProp === 'undefined') { - prevProp = null as any; + prevProp = null; } } @@ -313,7 +317,7 @@ function diffProperties( // case: !Object is the default case if (defaultDiffer(prevProp, nextProp)) { // a normal leaf has changed - (updatePayload || (updatePayload = {} as {[string]: $FlowFixMe}))[ + (updatePayload || (updatePayload = {} as {[string]: unknown}))[ propKey ] = nextProp; } @@ -333,7 +337,7 @@ function diffProperties( ? // $FlowFixMe[incompatible-use] found when upgrading Flow attributeConfig.process(nextProp) : nextProp; - (updatePayload || (updatePayload = {} as {[string]: $FlowFixMe}))[ + (updatePayload || (updatePayload = {} as {[string]: unknown}))[ propKey ] = nextValue; } @@ -345,14 +349,19 @@ function diffProperties( // this point so we assume it must be AttributeConfiguration. updatePayload = diffNestedProperty( updatePayload, + // $FlowFixMe[incompatible-type] prop values are typed `unknown` but are nodes in this nested-config path prevProp, + // $FlowFixMe[incompatible-type] prop values are typed `unknown` but are nodes in this nested-config path nextProp, + // $FlowFixMe[unclear-type] AttributeConfiguration/AnyAttributeType are defined upstream with $FlowFixMe attributeConfig as any as AttributeConfiguration, ); if (removedKeyCount > 0 && updatePayload) { restoreDeletedValuesInNestedArray( updatePayload, + // $FlowFixMe[incompatible-type] prop values are typed `unknown` but are nodes in this nested-config path nextProp, + // $FlowFixMe[unclear-type] AttributeConfiguration/AnyAttributeType are defined upstream with $FlowFixMe attributeConfig as any as AttributeConfiguration, ); removedKeys = null; @@ -389,9 +398,8 @@ function diffProperties( ) { // case: CustomAttributeConfiguration | !Object // Flag the leaf property for removal by sending a sentinel. - (updatePayload || (updatePayload = {} as {[string]: $FlowFixMe}))[ - propKey - ] = null; + (updatePayload || (updatePayload = {} as {[string]: unknown}))[propKey] = + null; if (!removedKeys) { removedKeys = {} as {[string]: boolean}; } @@ -405,7 +413,9 @@ function diffProperties( // were removed so we need to go through and clear out all of them. updatePayload = clearNestedProperty( updatePayload, + // $FlowFixMe[incompatible-type] prop values are typed `unknown` but are nodes in this nested-config path prevProp, + // $FlowFixMe[unclear-type] AttributeConfiguration/AnyAttributeType are defined upstream with $FlowFixMe attributeConfig as any as AttributeConfiguration, ); } @@ -414,10 +424,11 @@ function diffProperties( } function addNestedProperty( - payload: null | Object, - props: Object, + payloadInput: null | {[string]: unknown}, + props: NestedNode, validAttributes: AttributeConfiguration, -): null | Object { +): null | {[string]: unknown} { + let payload = payloadInput; // Flatten nested style props. if (Array.isArray(props)) { for (let i = 0; i < props.length; i++) { @@ -431,6 +442,7 @@ function addNestedProperty( const attributeConfig = validAttributes[ propKey + // $FlowFixMe[unclear-type] AttributeConfiguration/AnyAttributeType are defined upstream with $FlowFixMe ] as any as AttributeConfiguration; if (attributeConfig == null) { @@ -466,13 +478,18 @@ function addNestedProperty( if (newValue !== undefined) { if (!payload) { - payload = {} as {[string]: $FlowFixMe}; + payload = {} as {[string]: unknown}; } payload[propKey] = newValue; continue; } - payload = addNestedProperty(payload, prop, attributeConfig); + payload = addNestedProperty( + payload, + // $FlowFixMe[incompatible-type] prop values are typed `unknown` but are nodes here + prop, + attributeConfig, + ); } return payload; @@ -483,25 +500,25 @@ function addNestedProperty( * to the payload for each valid key. */ function clearProperties( - updatePayload: null | Object, - prevProps: Object, + updatePayload: null | {[string]: unknown}, + prevProps: {[string]: unknown}, validAttributes: AttributeConfiguration, -): null | Object { +): null | {[string]: unknown} { return diffProperties(updatePayload, prevProps, emptyObject, validAttributes); } export function create( - props: Object, + props: NestedNode, validAttributes: AttributeConfiguration, -): null | Object { +): null | {[string]: unknown} { return addNestedProperty(null, props, validAttributes); } export function diff( - prevProps: Object, - nextProps: Object, + prevProps: {[string]: unknown}, + nextProps: {[string]: unknown}, validAttributes: AttributeConfiguration, -): null | Object { +): null | {[string]: unknown} { return diffProperties( null, // updatePayload prevProps, diff --git a/packages/react-native/Libraries/ReactNative/UIManager.js b/packages/react-native/Libraries/ReactNative/UIManager.js index a97c6a7b3ea7..a020393458ab 100644 --- a/packages/react-native/Libraries/ReactNative/UIManager.js +++ b/packages/react-native/Libraries/ReactNative/UIManager.js @@ -4,7 +4,7 @@ * This source code is licensed under the MIT license found in the * LICENSE file in the root directory of this source tree. * - * @flow + * @flow strict-local * @format */ @@ -60,7 +60,7 @@ const UIManager: UIManagerJSInterface = { const FabricUIManager = nullthrows(getFabricUIManager()); const shadowNode = FabricUIManager.findShadowNodeByTag_DEPRECATED(reactTag); - if (shadowNode) { + if (shadowNode != null) { FabricUIManager.measure(shadowNode, callback); } else { console.warn(`measure cannot find view with tag #${reactTag}`); @@ -103,7 +103,7 @@ const UIManager: UIManagerJSInterface = { const FabricUIManager = nullthrows(getFabricUIManager()); const shadowNode = FabricUIManager.findShadowNodeByTag_DEPRECATED(reactTag); - if (shadowNode) { + if (shadowNode != null) { FabricUIManager.measureInWindow(shadowNode, callback); } else { console.warn(`measure cannot find view with tag #${reactTag}`); @@ -129,7 +129,7 @@ const UIManager: UIManagerJSInterface = { measureLayout( reactTag: number, ancestorReactTag: number, - errorCallback: (error: Object) => void, + errorCallback, callback: ( left: number, top: number, @@ -144,7 +144,7 @@ const UIManager: UIManagerJSInterface = { const ancestorShadowNode = FabricUIManager.findShadowNodeByTag_DEPRECATED(ancestorReactTag); - if (!shadowNode || !ancestorShadowNode) { + if (shadowNode == null || ancestorShadowNode == null) { return; } @@ -167,7 +167,7 @@ const UIManager: UIManagerJSInterface = { measureLayoutRelativeToParent( reactTag: number, - errorCallback: (error: Object) => void, + errorCallback, callback: ( left: number, top: number, @@ -182,7 +182,7 @@ const UIManager: UIManagerJSInterface = { const FabricUIManager = nullthrows(getFabricUIManager()); const shadowNode = FabricUIManager.findShadowNodeByTag_DEPRECATED(reactTag); - if (shadowNode) { + if (shadowNode != null) { FabricUIManager.measure( shadowNode, (left, top, width, height, pageX, pageY) => { @@ -210,7 +210,7 @@ const UIManager: UIManagerJSInterface = { dispatchViewManagerCommand( reactTag: number, commandName: number | string, - commandArgs: any[], + commandArgs: Array, ) { // Sometimes, libraries directly pass in the output of `findNodeHandle` to // this function without checking if it's null. This guards against that @@ -224,12 +224,16 @@ const UIManager: UIManagerJSInterface = { const FabricUIManager = nullthrows(getFabricUIManager()); const shadowNode = FabricUIManager.findShadowNodeByTag_DEPRECATED(reactTag); - if (shadowNode) { + if (shadowNode != null) { // Transform the accidental CommandID into a CommandName which is the stringified number. // The interop layer knows how to convert this number into the right method name. // Stringify a string is a no-op, so it's safe. - commandName = `${commandName}`; - FabricUIManager.dispatchCommand(shadowNode, commandName, commandArgs); + const resolvedCommandName = `${commandName}`; + FabricUIManager.dispatchCommand( + shadowNode, + resolvedCommandName, + commandArgs, + ); } } else { UIManagerImpl.dispatchViewManagerCommand( diff --git a/packages/react-native/Libraries/ReactNative/getNativeComponentAttributes.js b/packages/react-native/Libraries/ReactNative/getNativeComponentAttributes.js index 8bd3c74483e3..d42571f88d09 100644 --- a/packages/react-native/Libraries/ReactNative/getNativeComponentAttributes.js +++ b/packages/react-native/Libraries/ReactNative/getNativeComponentAttributes.js @@ -4,12 +4,14 @@ * This source code is licensed under the MIT license found in the * LICENSE file in the root directory of this source tree. * - * @flow + * @flow strict-local * @format */ 'use strict'; +import type {ViewManagerConfig} from './NativeUIManager'; + import processBoxShadow from '../StyleSheet/processBoxShadow'; const ReactNativeStyleAttributes = @@ -35,7 +37,9 @@ const sizesDiffer = require('../Utilities/differ/sizesDiffer').default; const UIManager = require('./UIManager').default; const nullthrows = require('nullthrows'); -function getNativeComponentAttributes(uiViewClassName: string): any { +function getNativeComponentAttributes( + uiViewClassName: string, +): ViewManagerConfig { const viewConfig = UIManager.getViewManagerConfig(uiViewClassName); if (viewConfig == null) { @@ -106,21 +110,20 @@ function getNativeComponentAttributes(uiViewClassName: string): any { directEventTypes, }); - attachDefaultEventTypes(viewConfig); - - return viewConfig; + return attachDefaultEventTypes(viewConfig); } -function attachDefaultEventTypes(viewConfig: any) { +function attachDefaultEventTypes( + viewConfig: ViewManagerConfig, +): ViewManagerConfig { // This is supported on UIManager platforms (ex: Android), // as lazy view managers are not implemented for all platforms. // See [UIManager] for details on constants and implementations. const constants = UIManager.getConstants(); if (constants.ViewManagerNames || constants.LazyViewManagersEnabled) { // Lazy view managers enabled. - viewConfig = merge( - viewConfig, - nullthrows(UIManager.getDefaultEventTypes)(), + return nullthrows( + merge(viewConfig, nullthrows(UIManager.getDefaultEventTypes)()), ); } else { viewConfig.bubblingEventTypes = merge( @@ -131,11 +134,15 @@ function attachDefaultEventTypes(viewConfig: any) { viewConfig.directEventTypes, constants.genericDirectEventTypes, ); + return viewConfig; } } // TODO: Figure out how to avoid all this runtime initialization cost. -function merge(destination: ?Object, source: ?Object): ?Object { +function merge( + destination: ?ViewManagerConfig, + source: ?ViewManagerConfig, +): ?ViewManagerConfig { if (!source) { return destination; } @@ -165,7 +172,12 @@ function merge(destination: ?Object, source: ?Object): ?Object { function getDifferForType( typeName: string, -): ?(prevProp: any, nextProp: any) => boolean { +): ?( + | typeof insetsDiffer + | typeof matricesDiffer + | typeof pointsDiffer + | typeof sizesDiffer +) { switch (typeName) { // iOS Types case 'CATransform3D': @@ -188,7 +200,18 @@ function getDifferForType( function getProcessorForAttribute( attributeName: string, typeName: string, -): ?(nextProp: any) => any { +): ?( + | typeof processBackgroundImage + | typeof processBackgroundPosition + | typeof processBackgroundRepeat + | typeof processBackgroundSize + | typeof processBoxShadow + | typeof processColor + | typeof processColorArray + | typeof processFilter + | typeof processFontVariationSettings + | typeof resolveAssetSource +) { if (attributeName === 'fontVariationSettings') { return processFontVariationSettings; } diff --git a/packages/react-native/Libraries/ReactNative/requireNativeComponent.js b/packages/react-native/Libraries/ReactNative/requireNativeComponent.js index 96016159f59d..080380e0e86c 100644 --- a/packages/react-native/Libraries/ReactNative/requireNativeComponent.js +++ b/packages/react-native/Libraries/ReactNative/requireNativeComponent.js @@ -4,7 +4,7 @@ * This source code is licensed under the MIT license found in the * LICENSE file in the root directory of this source tree. * - * @flow + * @flow strict-local * @format */ @@ -29,8 +29,10 @@ const getNativeComponentAttributes = const requireNativeComponent = ( uiViewClassName: string, ): HostComponent => - createReactNativeComponentClass(uiViewClassName, () => - getNativeComponentAttributes(uiViewClassName), + createReactNativeComponentClass( + uiViewClassName, + () => getNativeComponentAttributes(uiViewClassName), + // $FlowFixMe[unclear-type] createReactNativeComponentClass returns the registered view name (a string) that Fabric resolves to this host component at runtime ) as any as HostComponent; export default requireNativeComponent; diff --git a/packages/react-native/Libraries/Settings/Settings.ios.js b/packages/react-native/Libraries/Settings/Settings.ios.js index 293fa9612cad..a987960239a6 100644 --- a/packages/react-native/Libraries/Settings/Settings.ios.js +++ b/packages/react-native/Libraries/Settings/Settings.ios.js @@ -4,7 +4,7 @@ * This source code is licensed under the MIT license found in the * LICENSE file in the root directory of this source tree. * - * @flow + * @flow strict-local * @format */ @@ -12,9 +12,17 @@ import RCTDeviceEventEmitter from '../EventEmitter/RCTDeviceEventEmitter'; import NativeSettingsManager from './NativeSettingsManager'; import invariant from 'invariant'; +type SettingsValues = {[string]: unknown, ...}; +type SettingsCallback = () => void; + +declare function castSetting(value: unknown): T; +function castSetting(value: unknown) { + return value; +} + const subscriptions: Array<{ keys: Array, - callback: ?Function, + callback: ?SettingsCallback, ... }> = []; @@ -25,26 +33,31 @@ const subscriptions: Array<{ * @see https://reactnative.dev/docs/settings * @platform ios */ -const Settings = { - _settings: (NativeSettingsManager && - NativeSettingsManager.getConstants().settings) as any, +const Settings: { + _settings: SettingsValues, + get(key: string): ?T, + set(settings: SettingsValues): void, + watchKeys(keys: string | Array, callback: SettingsCallback): number, + clearWatch(watchId: number): void, + _sendObservations(body: SettingsValues): void, +} = { + _settings: NativeSettingsManager.getConstants().settings, /** * Get the current value for the given key. */ - get(key: string): unknown { - // $FlowFixMe[object-this-reference] - return this._settings[key]; + get(key: string): ?T { + return castSetting(Settings._settings[key]); }, /** * Set one or more values by merging the provided object into the current * settings. */ - set(settings: Object) { - // $FlowFixMe[object-this-reference] - // $FlowFixMe[unsafe-object-assign] - this._settings = Object.assign(this._settings, settings); + set(settings: SettingsValues) { + Object.keys(settings).forEach(key => { + Settings._settings[key] = settings[key]; + }); NativeSettingsManager.setValues(settings); }, @@ -53,18 +66,16 @@ const Settings = { * whenever a watched key's value changes. Returns a `watchId` that can be * passed to `clearWatch` to unsubscribe. */ - watchKeys(keys: string | Array, callback: Function): number { - if (typeof keys === 'string') { - keys = [keys]; - } + watchKeys(keys: string | Array, callback: SettingsCallback): number { + const watchedKeys = typeof keys === 'string' ? [keys] : keys; invariant( - Array.isArray(keys), + Array.isArray(watchedKeys), 'keys should be a string or array of strings', ); const sid = subscriptions.length; - subscriptions.push({keys: keys, callback: callback}); + subscriptions.push({keys: watchedKeys, callback}); return sid; }, @@ -77,13 +88,11 @@ const Settings = { } }, - _sendObservations(body: Object) { + _sendObservations(body: SettingsValues) { Object.keys(body).forEach(key => { const newValue = body[key]; - // $FlowFixMe[object-this-reference] - const didChange = this._settings[key] !== newValue; - // $FlowFixMe[object-this-reference] - this._settings[key] = newValue; + const didChange = Settings._settings[key] !== newValue; + Settings._settings[key] = newValue; if (didChange) { subscriptions.forEach(sub => { diff --git a/packages/react-native/Libraries/Settings/Settings.js b/packages/react-native/Libraries/Settings/Settings.js index b0ec5480569f..81150f02e58f 100644 --- a/packages/react-native/Libraries/Settings/Settings.js +++ b/packages/react-native/Libraries/Settings/Settings.js @@ -4,15 +4,15 @@ * This source code is licensed under the MIT license found in the * LICENSE file in the root directory of this source tree. * - * @flow + * @flow strict-local * @format */ import Platform from '../Utilities/Platform'; let Settings: { - get(key: string): any, - set(settings: Object): void, + get(key: string): ?T, + set(settings: {[string]: unknown, ...}): void, watchKeys(keys: string | Array, callback: () => void): number, clearWatch(watchId: number): void, ... diff --git a/packages/react-native/Libraries/Settings/SettingsFallback.js b/packages/react-native/Libraries/Settings/SettingsFallback.js index bf00f04e16d6..3543585523a2 100644 --- a/packages/react-native/Libraries/Settings/SettingsFallback.js +++ b/packages/react-native/Libraries/Settings/SettingsFallback.js @@ -4,19 +4,19 @@ * This source code is licensed under the MIT license found in the * LICENSE file in the root directory of this source tree. * - * @flow + * @flow strict-local * @format */ 'use strict'; const Settings = { - get(key: string): any { + get(key: string): ?T { console.warn('Settings is not yet supported on this platform.'); return null; }, - set(settings: Object) { + set(settings: {[string]: unknown, ...}) { console.warn('Settings is not yet supported on this platform.'); }, diff --git a/packages/react-native/Libraries/StyleSheet/processTransform.js b/packages/react-native/Libraries/StyleSheet/processTransform.js index d4371e2b7c8c..d9085c418e2e 100644 --- a/packages/react-native/Libraries/StyleSheet/processTransform.js +++ b/packages/react-native/Libraries/StyleSheet/processTransform.js @@ -4,7 +4,7 @@ * This source code is licensed under the MIT license found in the * LICENSE file in the root directory of this source tree. * - * @flow + * @flow strict-local * @format */ @@ -13,6 +13,9 @@ const stringifySafe = require('../Utilities/stringifySafe').default; const invariant = require('invariant'); +type TransformObject = {[string]: unknown, ...}; +type TransformArray = Array; + /** * Generate a transform matrix based on the provided transforms, and use that * within the style object instead. @@ -21,12 +24,11 @@ const invariant = require('invariant'); * be applied in an arbitrary order, and yet have a universal, singular * interface to native code. */ -function processTransform( - transform: Array | string, -): Array | Array { +function processTransform(transform: TransformArray | string): TransformArray { + let normalizedTransform; if (typeof transform === 'string') { const regex = new RegExp(/(\w+)\(([^)]+)\)/g); - const transformArray: Array = []; + const transformArray: TransformArray = []; let matches; while ((matches = regex.exec(transform))) { @@ -39,14 +41,16 @@ function processTransform( transformArray.push({[key]: value}); } } - transform = transformArray; + normalizedTransform = transformArray; + } else { + normalizedTransform = transform; } if (__DEV__) { - _validateTransforms(transform); + _validateTransforms(normalizedTransform); } - return transform; + return normalizedTransform; } const _getKeyAndValueFromCSSTransform: ( @@ -138,7 +142,7 @@ const _getKeyAndValueFromCSSTransform: ( } }; -function _validateTransforms(transform: Array): void { +function _validateTransforms(transform: TransformArray): void { transform.forEach(transformation => { const keys = Object.keys(transformation); invariant( @@ -160,11 +164,16 @@ function _validateTransforms(transform: Array): void { function _validateTransform( key: string, - value: any | number | string, - transformation: any, + value: unknown, + transformation: TransformObject, ) { + const isAnimatedValue = + value != null && + typeof value === 'object' && + 'getValue' in value && + typeof value.getValue === 'function'; invariant( - !value.getValue, + !isAnimatedValue, 'You passed an Animated.Value to a normal component. ' + 'You need to wrap that component in an Animated. For example, ' + 'replace by .', @@ -181,24 +190,30 @@ function _validateTransform( } switch (key) { case 'matrix': + invariant( + Array.isArray(value), + 'Transform with key of %s must have an array as the value: %s', + key, + stringifySafe(transformation), + ); invariant( value.length === 9 || value.length === 16, 'Matrix transform must have a length of 9 (2d) or 16 (3d). ' + 'Provided matrix has a length of %s: %s', - /* $FlowFixMe[prop-missing] (>=0.84.0 site=react_native_fb) This - * comment suppresses an error found when Flow v0.84 was deployed. To - * see the error, delete this comment and run Flow. */ value.length, stringifySafe(transformation), ); break; case 'translate': + invariant( + Array.isArray(value), + 'Transform with key of %s must have an array as the value: %s', + key, + stringifySafe(transformation), + ); invariant( value.length === 2 || value.length === 3, 'Transform with key translate must be an array of length 2 or 3, found %s: %s', - /* $FlowFixMe[prop-missing] (>=0.84.0 site=react_native_fb) This - * comment suppresses an error found when Flow v0.84 was deployed. To - * see the error, delete this comment and run Flow. */ value.length, stringifySafe(transformation), ); diff --git a/packages/react-native/Libraries/Text/TextNativeComponent.js b/packages/react-native/Libraries/Text/TextNativeComponent.js index 11c869113337..50815d71d91f 100644 --- a/packages/react-native/Libraries/Text/TextNativeComponent.js +++ b/packages/react-native/Libraries/Text/TextNativeComponent.js @@ -4,7 +4,7 @@ * This source code is licensed under the MIT license found in the * LICENSE file in the root directory of this source tree. * - * @flow + * @flow strict-local * @format */ @@ -77,23 +77,31 @@ const virtualTextViewConfig = { // Additional note: Our long term plan is to reduce the overhead of the // and wrappers so that we no longer have any reason to export these APIs. export const NativeText: HostComponent = - createReactNativeComponentClass('RCTText', () => - createViewConfig(textViewConfig), + createReactNativeComponentClass( + 'RCTText', + () => createViewConfig(textViewConfig), + // $FlowFixMe[unclear-type] ) as any; export const NativeVirtualText: HostComponent = - !global.RN$Bridgeless && !UIManager.hasViewManagerConfig('RCTVirtualText') + global.RN$Bridgeless !== true && + !UIManager.hasViewManagerConfig('RCTVirtualText') ? NativeText - : (createReactNativeComponentClass('RCTVirtualText', () => - createViewConfig(virtualTextViewConfig), + : (createReactNativeComponentClass( + 'RCTVirtualText', + () => createViewConfig(virtualTextViewConfig), + // $FlowFixMe[unclear-type] ) as any); export const NativeSelectableText: HostComponent = enablePreparedTextLayout() - ? (createReactNativeComponentClass('RCTSelectableText', () => - createViewConfig({ - ...textViewConfig, - uiViewClassName: 'RCTSelectableText', - }), + ? (createReactNativeComponentClass( + 'RCTSelectableText', + () => + createViewConfig({ + ...textViewConfig, + uiViewClassName: 'RCTSelectableText', + }), + // $FlowFixMe[unclear-type] ) as any) : NativeText; diff --git a/packages/react-native/Libraries/Types/UIManagerJSInterface.js b/packages/react-native/Libraries/Types/UIManagerJSInterface.js index 26d59a2c13c4..31a07cb1f807 100644 --- a/packages/react-native/Libraries/Types/UIManagerJSInterface.js +++ b/packages/react-native/Libraries/Types/UIManagerJSInterface.js @@ -4,13 +4,13 @@ * This source code is licensed under the MIT license found in the * LICENSE file in the root directory of this source tree. * - * @flow + * @flow strict-local * @format */ -import type {Spec} from '../ReactNative/NativeUIManager'; +import type {Spec, ViewManagerConfig} from '../ReactNative/NativeUIManager'; export interface UIManagerJSInterface extends Spec { - readonly getViewManagerConfig: (viewManagerName: string) => Object; + readonly getViewManagerConfig: (viewManagerName: string) => ViewManagerConfig; readonly hasViewManagerConfig: (viewManagerName: string) => boolean; } diff --git a/packages/react-native/Libraries/Utilities/Dimensions.js b/packages/react-native/Libraries/Utilities/Dimensions.js index 85a6fc5dc494..121cffce9e42 100644 --- a/packages/react-native/Libraries/Utilities/Dimensions.js +++ b/packages/react-native/Libraries/Utilities/Dimensions.js @@ -4,7 +4,7 @@ * This source code is licensed under the MIT license found in the * LICENSE file in the root directory of this source tree. * - * @flow + * @flow strict-local * @format */ @@ -21,14 +21,39 @@ import invariant from 'invariant'; export type {DimensionsPayload, DisplayMetrics, DisplayMetricsAndroid}; +export type DimensionsChangePayload = Readonly<{ + window: DisplayMetrics, + screen: DisplayMetrics, +}>; + /** @deprecated Use DisplayMetrics */ export type ScaledSize = DisplayMetrics; const eventEmitter = new EventEmitter<{ - change: [DimensionsPayload], + change: [DimensionsChangePayload], }>(); let dimensionsInitialized = false; -let dimensions: DimensionsPayload; +let dimensions: DimensionsChangePayload; + +declare function addDimensionsEventListener( + type: 'change', + handler: (dimensions: DimensionsChangePayload) => void, +): EventSubscription; +declare function addDimensionsEventListener( + type: 'change', + handler: (dimensions: {window: {width: number, height: number}}) => void, +): EventSubscription; +function addDimensionsEventListener( + type: 'change', + handler: (dimensions: DimensionsChangePayload) => void, +) { + invariant( + type === 'change', + 'Trying to subscribe to unknown event: "%s"', + type, + ); + return eventEmitter.addListener(type, handler); +} /** * Provides the application window's width and height. Prefer @@ -91,6 +116,9 @@ class Dimensions { screen = window; } + invariant(window != null, 'Dimensions must define window metrics'); + invariant(screen != null, 'Dimensions must define screen metrics'); + dimensions = {window, screen}; if (dimensionsInitialized) { // Don't fire 'change' the first time the dimensions are set. @@ -109,17 +137,8 @@ class Dimensions { * `screen` properties whose values are the same as the return values of * `Dimensions.get('window')` and `Dimensions.get('screen')`, respectively. */ - static addEventListener( - type: 'change', - handler: Function, - ): EventSubscription { - invariant( - type === 'change', - 'Trying to subscribe to unknown event: "%s"', - type, - ); - return eventEmitter.addListener(type, handler); - } + static addEventListener: typeof addDimensionsEventListener = + addDimensionsEventListener; } // Subscribe before calling getConstants to make sure we don't miss any updates in between. diff --git a/packages/react-native/Libraries/Utilities/ReactNativeTestTools.js b/packages/react-native/Libraries/Utilities/ReactNativeTestTools.js index a29fed1f0efb..ce32da6e921c 100644 --- a/packages/react-native/Libraries/Utilities/ReactNativeTestTools.js +++ b/packages/react-native/Libraries/Utilities/ReactNativeTestTools.js @@ -4,7 +4,7 @@ * This source code is licensed under the MIT license found in the * LICENSE file in the root directory of this source tree. * - * @flow + * @flow strict-local * @format */ @@ -163,7 +163,9 @@ function maximumDepthOfJSON(node: ?ReactTestRendererJSON): number { } } -function renderAndEnforceStrictMode(element: React.Node): any { +function renderAndEnforceStrictMode( + element: React.Node, +): ReactTestRendererType { expectNoConsoleError(); return renderWithStrictMode(element); } @@ -217,8 +219,13 @@ function scrollToBottom(instance: ReactTestInstance) { // To make error messages a little bit better, we attach a custom toString // implementation to a predicate function withMessage(fn: Predicate, message: string): Predicate { - (fn as any).toString = () => message; - return fn; + return new Proxy(fn, { + get(target, property, receiver) { + return property === 'toString' + ? () => message + : Reflect.get(target, property, receiver); + }, + }); } export {byClickable}; diff --git a/packages/react-native/Libraries/Utilities/codegenNativeCommands.js b/packages/react-native/Libraries/Utilities/codegenNativeCommands.js index 4ef170fd7d2f..ed7ef1e902fb 100644 --- a/packages/react-native/Libraries/Utilities/codegenNativeCommands.js +++ b/packages/react-native/Libraries/Utilities/codegenNativeCommands.js @@ -4,30 +4,40 @@ * This source code is licensed under the MIT license found in the * LICENSE file in the root directory of this source tree. * - * @flow + * @flow strict-local * @format */ +import type {HostInstance} from '../../src/private/types/HostInstance'; + const {dispatchCommand} = require('../ReactNative/RendererProxy'); type NativeCommandsOptions = Readonly<{ supportedCommands: ReadonlyArray, }>; +declare function castCommandObject(commandObj: { + [keyof T]: (...ReadonlyArray) => void, +}): T; +function castCommandObject(commandObj: interface {}) { + return commandObj; +} + function codegenNativeCommands( - options: NativeCommandsOptions, + options: NativeCommandsOptions, ): T { const commandObj: {[keyof T]: (...ReadonlyArray) => void} = {}; options.supportedCommands.forEach(command => { - // $FlowFixMe[missing-local-annot] - commandObj[command] = (ref, ...args) => { - // $FlowFixMe[incompatible-type] + commandObj[command] = ( + ref: HostInstance, + ...args: Array + ): void => { dispatchCommand(ref, command, args); }; }); - return commandObj as any as T; + return castCommandObject(commandObj); } export default codegenNativeCommands; diff --git a/packages/react-native/Libraries/Utilities/differ/__tests__/deepDiffer-itest.js b/packages/react-native/Libraries/Utilities/differ/__tests__/deepDiffer-itest.js index 2625e2504848..9bcd0624eeb6 100644 --- a/packages/react-native/Libraries/Utilities/differ/__tests__/deepDiffer-itest.js +++ b/packages/react-native/Libraries/Utilities/differ/__tests__/deepDiffer-itest.js @@ -136,7 +136,7 @@ describe('deepDiffer', function () { expect( deepDiffer( () => {}, - x => x, + (x: unknown) => x, ), ).toBe(false); const f = () => {}; @@ -146,7 +146,7 @@ describe('deepDiffer', function () { expect( deepDiffer( () => {}, - x => x, + (x: unknown) => x, undefined, {unsafelyIgnoreFunctions: false}, ), @@ -160,7 +160,7 @@ describe('deepDiffer', function () { expect( deepDiffer( () => {}, - x => x, + (x: unknown) => x, {unsafelyIgnoreFunctions: false}, ), ).toBe(true); diff --git a/packages/react-native/Libraries/Utilities/differ/deepDiffer.js b/packages/react-native/Libraries/Utilities/differ/deepDiffer.js index 0f5d48cc7270..4a9959d10a11 100644 --- a/packages/react-native/Libraries/Utilities/differ/deepDiffer.js +++ b/packages/react-native/Libraries/Utilities/differ/deepDiffer.js @@ -4,7 +4,7 @@ * This source code is licensed under the MIT license found in the * LICENSE file in the root directory of this source tree. * - * @flow + * @flow strict-local * @format */ @@ -29,8 +29,8 @@ function unstable_setLogListeners(listeners: ?LogListeners) { * @returns {bool} true if different, false if equal */ function deepDiffer( - one: any, - two: any, + one: unknown, + two: unknown, maxDepthOrOptions: Options | number = -1, maybeOptions?: Options, ): boolean { @@ -73,7 +73,9 @@ function deepDiffer( return true; } if (Array.isArray(one)) { - // We know two is also an array because the constructors are equal + if (!Array.isArray(two)) { + return true; + } const len = one.length; if (two.length !== len) { return true; diff --git a/packages/react-native/Libraries/Utilities/useWindowDimensions.js b/packages/react-native/Libraries/Utilities/useWindowDimensions.js index 48c689116154..02e35b6df780 100644 --- a/packages/react-native/Libraries/Utilities/useWindowDimensions.js +++ b/packages/react-native/Libraries/Utilities/useWindowDimensions.js @@ -27,9 +27,10 @@ export default function useWindowDimensions(): useEffect(() => { function handleChange({ window, - }: { + }: Readonly<{ window: DisplayMetrics | DisplayMetricsAndroid, - }) { + ... + }>) { if ( dimensions.width !== window.width || dimensions.height !== window.height || diff --git a/packages/react-native/ReactNativeApi.d.ts b/packages/react-native/ReactNativeApi.d.ts index e1654b681c24..7a94ffdaf350 100644 --- a/packages/react-native/ReactNativeApi.d.ts +++ b/packages/react-native/ReactNativeApi.d.ts @@ -4,7 +4,7 @@ * This source code is licensed under the MIT license found in the * LICENSE file in the root directory of this source tree. * - * @generated SignedSource<> + * @generated SignedSource<<3bfb5ea9619ed322832d18e8c158b5b8>> * * This file was generated by scripts/js-api/build-types/index.js. */ @@ -337,8 +337,10 @@ declare const RCTNetworking_default: { method: string, trackingName: string | void, url: string, - headers: {}, - data: RequestBody, + headers: { + [$$Key$$: string]: string + }, + data: RequestBody | undefined, responseType: NativeResponseType, incrementalUpdates: boolean, timeout: number, @@ -379,8 +381,8 @@ declare const setStyleAttributePreprocessor: ( declare const Settings: typeof Settings_default declare let Settings_default: { clearWatch(watchId: number): void - get(key: string): any - set(settings: Object): void + get(key: string): T | undefined + set(settings: { [$$Key$$: string]: unknown }): void watchKeys(keys: Array | string, callback: () => void): number } declare const spring: typeof $$AnimatedImplementation.spring @@ -1114,6 +1116,23 @@ declare type add = typeof add declare function addChangeListener( listener: (preferences: AppearancePreferences) => void, ): EventSubscription +declare function addDimensionsEventListener( + type: "change", + handler: (dimensions: DimensionsChangePayload) => void, +): EventSubscription +declare function addDimensionsEventListener( + type: "change", + handler: (dimensions: { + window: { + height: number + width: number + } + }) => void, +): EventSubscription +declare function addDimensionsEventListener( + type: "change", + handler: (dimensions: DimensionsChangePayload) => void, +): void declare class Alert { static alert( title: null | string | undefined, @@ -1728,7 +1747,7 @@ declare class CellRenderMask { declare type Clipboard = typeof Clipboard declare type codegenNativeCommands = typeof codegenNativeCommands declare function codegenNativeCommands_default( - options: NativeCommandsOptions, + options: NativeCommandsOptions, ): T declare type codegenNativeComponent = typeof codegenNativeComponent declare function codegenNativeComponent_default( @@ -1877,10 +1896,14 @@ declare type DevMenuStatic = { declare type DevSettings = typeof DevSettings declare type diffClamp = typeof diffClamp declare class Dimensions { - static addEventListener(type: "change", handler: Function): EventSubscription + static addEventListener: typeof addDimensionsEventListener static get(dim: string): DisplayMetrics | DisplayMetricsAndroid static set(dims: Readonly): void } +declare type DimensionsChangePayload = { + readonly screen: DisplayMetrics + readonly window: DisplayMetrics +} declare type DimensionsPayload = { screen?: DisplayMetrics screenPhysicalPixels?: DisplayMetricsAndroid @@ -2213,7 +2236,7 @@ declare class FlatList extends React.PureComponent< constructor(props: FlatListProps) flashScrollIndicators(): void getNativeScrollRef(): null | ScrollViewInstance | undefined - getScrollableNode(): any + getScrollableNode(): null | number | undefined getScrollResponder(): null | ScrollResponderType | undefined recordInteraction(): void render(): React.ReactNode @@ -3273,7 +3296,7 @@ declare type OnAnimationDidFailCallback = () => void declare type OpaqueColorValue = NativeColorValue declare type OptionalFlatListProps = { columnWrapperStyle?: ViewStyleProp - extraData?: any + extraData?: unknown fadingEdgeLength?: | (number | undefined) | { @@ -3300,7 +3323,7 @@ declare type OptionalFlatListProps = { } declare type OptionalPlatformSelectSpec = { [key in PlatformOSType]?: T } declare type OptionalSectionListProps = { - extraData?: any + extraData?: unknown initialNumToRender?: number inverted?: boolean keyExtractor?: (item: ItemT, index: number) => string @@ -4086,9 +4109,7 @@ declare type RequestBody = | Blob_default | FormData_default | string - | { - uri: string - } + | URIRequestBody declare type RequiredFlatListProps = { data: Readonly> | undefined } @@ -4222,7 +4243,9 @@ declare type RootTag = symbol & { __RootTag__: string } declare type RootTagContext = typeof RootTagContext -declare type RootViewStyleProvider = (appParameters: Object) => ViewStyleProp +declare type RootViewStyleProvider = ( + appParameters: AppParameters, +) => ViewStyleProp declare function runApplication( appKey: string, appParameters: AppParameters, @@ -4418,12 +4441,12 @@ declare class SectionList< > extends React.PureComponent> { props: SectionListProps flashScrollIndicators(): void - getScrollableNode(): any + getScrollableNode(): null | number | undefined getScrollResponder(): null | ScrollResponderType | undefined recordInteraction(): void render(): React.ReactNode scrollToLocation(params: ScrollToLocationParamsType): void - setNativeProps(props: Object): void + setNativeProps(props: { [$$Key$$: string]: unknown }): void } declare type SectionListData< SectionItemT, @@ -4548,9 +4571,9 @@ declare interface Spec_2 extends TurboModule { readonly focus?: (reactTag: number) => void readonly getConstantsForViewManager?: ( viewManagerName: string, - ) => Object | undefined - readonly getDefaultEventTypes?: () => Array - readonly lazilyLoadView?: (name: string) => Object + ) => undefined | ViewManagerConfig + readonly getDefaultEventTypes?: () => ViewManagerConfig + readonly lazilyLoadView?: (name: string) => ViewManagerConfig readonly sendAccessibilityEvent?: ( reactTag: number, eventType: number, @@ -4558,20 +4581,20 @@ declare interface Spec_2 extends TurboModule { readonly setLayoutAnimationEnabledExperimental?: (enabled: boolean) => void readonly clearJSResponder: () => void readonly configureNextLayoutAnimation: ( - config: Object, + config: UnsafeObject, callback: () => void, - errorCallback: (error: Object) => void, + errorCallback: (error: UnsafeObject) => void, ) => void readonly createView: ( reactTag: number, viewName: string, rootTag: RootTag, - props: Object, + props: UnsafeObject, ) => void readonly dispatchViewManagerCommand: ( reactTag: number, commandID: number, - commandArgs?: Array, + commandArgs?: Array, ) => void readonly findSubviewIn: ( reactTag: number, @@ -4584,7 +4607,7 @@ declare interface Spec_2 extends TurboModule { height: number, ) => void, ) => void - readonly getConstants: () => Object + readonly getConstants: () => UIManagerConstants readonly manageChildren: ( containerTag: number, moveFromIndices: Array, @@ -4604,12 +4627,12 @@ declare interface Spec_2 extends TurboModule { readonly measureLayout: ( reactTag: number, ancestorReactTag: number, - errorCallback: (error: Object) => void, + errorCallback: (error: UnsafeObject) => void, callback: NativeMeasureLayoutOnSuccessCallback, ) => void readonly measureLayoutRelativeToParent: ( reactTag: number, - errorCallback: (error: Object) => void, + errorCallback: (error: UnsafeObject) => void, callback: ( left: number, top: number, @@ -4625,7 +4648,7 @@ declare interface Spec_2 extends TurboModule { readonly updateView: ( reactTag: number, viewName: string, - props: Object, + props: UnsafeObject, ) => void readonly viewIsDescendantOf: ( reactTag: number, @@ -5391,8 +5414,9 @@ declare type TVViewPropsIOS = { readonly tvParallaxTiltAngle?: number } declare type UIManager = typeof UIManager +declare type UIManagerConstants = UnsafeObject declare interface UIManagerJSInterface extends Spec_2 { - readonly getViewManagerConfig: (viewManagerName: string) => Object + readonly getViewManagerConfig: (viewManagerName: string) => ViewManagerConfig readonly hasViewManagerConfig: (viewManagerName: string) => boolean } declare type unforkEvent = typeof unforkEvent @@ -5405,6 +5429,13 @@ declare type UnsafeEventObject = Object declare type UnsafeMixed = unknown declare type UnsafeNativeEventObject = Object declare type UnsafeObject = Object +declare type URIRequestBody = { + readonly base64?: string + readonly blob?: BlobData + readonly formData?: Array + readonly string?: string + readonly uri: string +} declare function useAnimatedColor( inputValue?: InputValue, config?: AnimatedColorConfig | null | undefined, @@ -5539,6 +5570,7 @@ declare type ViewConfig = { readonly validAttributes: AttributeConfiguration } declare type ViewInstance = HostInstance +declare type ViewManagerConfig = UnsafeObject declare interface ViewProps extends Readonly< DirectEventProps & GestureResponderHandlers & @@ -5734,7 +5766,7 @@ declare function Wrapper_default( $$PARAM_0$$: ModalRefProps & ModalProps, ): React.ReactNode declare type WrapperComponentProvider = ( - appParameters: Object, + appParameters?: AppParameters, appKey?: string, ) => React.ComponentType export { @@ -5756,9 +5788,9 @@ export { AlertOptions, // 8a116d2a AlertType, // 5ab91217 AndroidKeyboardEvent, // e03becc8 - Animated, // b73ca27a + Animated, // 8f83ae86 AppConfig, // 35c0ca70 - AppRegistry, // eb82174d + AppRegistry, // 8ea6b37b AppState, // 12012be5 AppStateEvent, // 80f034c3 AppStateStatus, // 447e5ef2 @@ -5786,7 +5818,7 @@ export { DeviceInfo, // 0f5a517b DeviceInfoConstants, // 279e7858 DimensionValue, // b163a381 - Dimensions, // 980ef68c + Dimensions, // 7439fe08 DimensionsPayload, // 653bc26c DisplayMetrics, // 1dc35cef DisplayMetricsAndroid, // 872e62eb @@ -5808,9 +5840,9 @@ export { EventSubscription, // b8d084aa ExtendedExceptionData, // 5a6ccf5a FilterFunction, // bf24c0e3 - FlatList, // 31e4dbb8 - FlatListInstance, // e256df59 - FlatListProps, // d134a60c + FlatList, // 286065ec + FlatListInstance, // 46930d35 + FlatListProps, // 965a268c FocusEvent, // 850f1517 FontVariant, // 7c7558bb GestureResponderEvent, // 14d3e77a @@ -5896,7 +5928,7 @@ export { NativeSyntheticEvent, // 534aaa92 NativeTouchEvent, // 59b676df NativeUIEvent, // 44ac26ac - Networking, // bbc5be42 + Networking, // 2baeacec OpaqueColorValue, // 25f3fa5b PackagerAsset, // d1c88cf4 PanResponder, // e4df325a @@ -5942,7 +5974,7 @@ export { Role, // af7b889d RootTag, // 3cd10504 RootTagContext, // 38bfc8f6 - RootViewStyleProvider, // a4547094 + RootViewStyleProvider, // a29fccf7 Runnable, // 594dd93a Runnables, // 4367c557 SafeAreaView, // 2a5620cd @@ -5959,14 +5991,14 @@ export { ScrollViewPropsIOS, // 807cb4f6 ScrollViewScrollToOptions, // 3313411e SectionBase, // 9f13db00 - SectionList, // f7750d02 + SectionList, // cbc4f0b6 SectionListData, // 1a4de01a - SectionListInstance, // 6f7c0fa0 - SectionListProps, // d1569771 + SectionListInstance, // 2f4e7937 + SectionListProps, // 67081836 SectionListRenderItem, // 715b2086 SectionListRenderItemInfo, // 4a48a922 Separators, // 6a45f7e3 - Settings, // 2be0c61e + Settings, // 40b69683 Share, // e4591b32 ShareAction, // ead1004a ShareActionSheetError, // 55e4f451 @@ -6023,7 +6055,7 @@ export { TransformsStyle, // 65e70f18 TurboModule, // dfe29706 TurboModuleRegistry, // 4ace6db2 - UIManager, // afbcdf05 + UIManager, // 6a18fc05 UTFSequence, // ad625158 Vibration, // 31e4bbf8 View, // 14779d0c @@ -6039,8 +6071,8 @@ export { VirtualizedSectionList, // 9fd9cd61 VirtualizedSectionListInstance, // 12b706d5 VirtualizedSectionListProps, // 52c34787 - WrapperComponentProvider, // 9ef54e61 - codegenNativeCommands, // 628a7c0a + WrapperComponentProvider, // 9b4247f6 + codegenNativeCommands, // 322f3f4e codegenNativeComponent, // 520daa94 findNodeHandle, // 93f80214 processColor, // 6e877698 diff --git a/packages/react-native/src/private/devsupport/rndevtools/setUpFuseboxReactDevToolsDispatcher.js b/packages/react-native/src/private/devsupport/rndevtools/setUpFuseboxReactDevToolsDispatcher.js index 79f7f4d7688a..ac4cbccef8d3 100644 --- a/packages/react-native/src/private/devsupport/rndevtools/setUpFuseboxReactDevToolsDispatcher.js +++ b/packages/react-native/src/private/devsupport/rndevtools/setUpFuseboxReactDevToolsDispatcher.js @@ -8,7 +8,7 @@ * @format */ -type JSONValue = +export type JSONValue = | string | number | boolean diff --git a/packages/react-native/src/private/specs_DEPRECATED/modules/NativeUIManager.js b/packages/react-native/src/private/specs_DEPRECATED/modules/NativeUIManager.js index 59683ec59aec..f173b274fff5 100644 --- a/packages/react-native/src/private/specs_DEPRECATED/modules/NativeUIManager.js +++ b/packages/react-native/src/private/specs_DEPRECATED/modules/NativeUIManager.js @@ -4,15 +4,32 @@ * This source code is licensed under the MIT license found in the * LICENSE file in the root directory of this source tree. * - * @flow + * @flow strict-local * @format */ import type {RootTag} from '../../../../Libraries/TurboModule/RCTExport'; import type {TurboModule} from '../../../../Libraries/TurboModule/RCTExport'; +import type {UnsafeObject} from '../../../../Libraries/Types/CodegenTypes'; import * as TurboModuleRegistry from '../../../../Libraries/TurboModule/TurboModuleRegistry'; +/** + * The config object for a single native view manager. It is consumed + * dynamically (callers read arbitrary keys such as `Commands`, `Constants`, + * `NativeProps`, `bubblingEventTypes`, etc.), so it is intentionally left as an + * open object type. Keep this in sync with the shape produced by native. + */ +export type ViewManagerConfig = UnsafeObject; + +/** + * The constants object returned by `UIManager.getConstants()`. It is consumed + * dynamically (callers read arbitrary keys such as `ViewManagerNames`, + * `LazyViewManagersEnabled`, per-view-manager configs, etc.), so it is + * intentionally left as an open object type. + */ +export type UIManagerConstants = UnsafeObject; + export type NativeMeasureOnSuccessCallback = ( x: number, y: number, @@ -37,17 +54,17 @@ export type NativeMeasureLayoutOnSuccessCallback = ( ) => void; export interface Spec extends TurboModule { - readonly getConstants: () => Object; + readonly getConstants: () => UIManagerConstants; readonly createView: ( reactTag: number, viewName: string, rootTag: RootTag, - props: Object, + props: UnsafeObject, ) => void; readonly updateView: ( reactTag: number, viewName: string, - props: Object, + props: UnsafeObject, ) => void; readonly findSubviewIn: ( reactTag: number, @@ -70,7 +87,7 @@ export interface Spec extends TurboModule { readonly dispatchViewManagerCommand: ( reactTag: number, commandID: number, // number || string - commandArgs?: Array, + commandArgs?: Array, ) => void; /** * Determines the location on screen, width, and height of the given view and @@ -134,12 +151,12 @@ export interface Spec extends TurboModule { readonly measureLayout: ( reactTag: number, ancestorReactTag: number, - errorCallback: (error: Object) => void, + errorCallback: (error: UnsafeObject) => void, callback: NativeMeasureLayoutOnSuccessCallback, ) => void; readonly measureLayoutRelativeToParent: ( reactTag: number, - errorCallback: (error: Object) => void, + errorCallback: (error: UnsafeObject) => void, callback: ( left: number, top: number, @@ -153,9 +170,9 @@ export interface Spec extends TurboModule { ) => void; readonly clearJSResponder: () => void; readonly configureNextLayoutAnimation: ( - config: Object, + config: UnsafeObject, callback: () => void, // check what is returned here - errorCallback: (error: Object) => void, + errorCallback: (error: UnsafeObject) => void, ) => void; readonly setChildren: ( containerTag: number, @@ -171,8 +188,10 @@ export interface Spec extends TurboModule { ) => void; // Android only - readonly getConstantsForViewManager?: (viewManagerName: string) => ?Object; - readonly getDefaultEventTypes?: () => Array; + readonly getConstantsForViewManager?: ( + viewManagerName: string, + ) => ?ViewManagerConfig; + readonly getDefaultEventTypes?: () => ViewManagerConfig; /** * Automatically animates views to their new positions when the * next layout happens. @@ -192,7 +211,7 @@ export interface Spec extends TurboModule { ) => void; // ios only - readonly lazilyLoadView?: (name: string) => Object; // revisit return + readonly lazilyLoadView?: (name: string) => ViewManagerConfig; // revisit return readonly focus?: (reactTag: number) => void; readonly blur?: (reactTag: number) => void; } diff --git a/packages/rn-tester/js/examples/Dimensions/DimensionsExample.js b/packages/rn-tester/js/examples/Dimensions/DimensionsExample.js index 37b6c77b5260..6d62cf88e722 100644 --- a/packages/rn-tester/js/examples/Dimensions/DimensionsExample.js +++ b/packages/rn-tester/js/examples/Dimensions/DimensionsExample.js @@ -15,7 +15,7 @@ import * as React from 'react'; import {useEffect, useState} from 'react'; import {Button, Dimensions, View, useWindowDimensions} from 'react-native'; -type Props = {dim: string}; +type Props = {dim: 'screen' | 'window'}; function DimensionsSubscription(props: Props) { const [dims, setDims] = useState(() => Dimensions.get(props.dim));