From d7a2a393ed67dc4201c9413e25f9e291d18d7afe Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Rub=C3=A9n=20Norte?= Date: Tue, 8 Sep 2026 10:01:08 -0700 Subject: [PATCH 1/6] Make Lists, Networking, and UIManager modules Flow strict-local (#57726) Summary: Pull Request resolved: https://github.com/react/react-native/pull/57726 Upgrade the FlatList/SectionList list components, the networking modules (`XMLHttpRequest`, `FormData`, `RCTNetworking`, `convertRequestBody`), and the UIManager modules from `flow` to `flow strict-local`. Public type signatures are preserved exactly, so no consumers need updating; internal `any` and `Object` usages were replaced with accurate types wherever that did not change a public signature. The `headers` parameters on the `XHRInterceptor` interface keep their original `Object` type behind a scoped `$FlowFixMe[unclear-type]`. Flow types indexers invariantly, so any precise annotation would reject existing interceptor implementations that declare a different value type for the header map. Changelog: [Internal] Reviewed By: javache Differential Revision: D113763786 --- .../react-native/Libraries/Lists/FlatList.js | 25 +- .../Libraries/Lists/SectionList.js | 10 +- .../Libraries/Lists/SectionListModern.js | 248 ------------------ .../Libraries/Network/FormData.js | 2 +- .../Network/RCTNetworking.android.js | 31 ++- .../Libraries/Network/RCTNetworking.ios.js | 2 +- .../Libraries/Network/RCTNetworking.js.flow | 4 +- .../Libraries/Network/XMLHttpRequest.js | 86 +++--- .../Libraries/Network/convertRequestBody.js | 56 +++- .../ReactNative/BridgelessUIManager.js | 138 +++++++--- .../Libraries/ReactNative/PaperUIManager.js | 15 +- .../Libraries/ReactNative/UIManager.js | 26 +- .../getNativeComponentAttributes.js | 47 +++- .../Libraries/Types/UIManagerJSInterface.js | 6 +- packages/react-native/ReactNativeApi.d.ts | 73 +++--- .../modules/NativeUIManager.js | 43 ++- 16 files changed, 371 insertions(+), 441 deletions(-) delete mode 100644 packages/react-native/Libraries/Lists/SectionListModern.js diff --git a/packages/react-native/Libraries/Lists/FlatList.js b/packages/react-native/Libraries/Lists/FlatList.js index ff4563be2406..cf9697226e24 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,7 +78,7 @@ 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 @@ -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) === false, + '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/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..a1508b6c6fe3 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,19 +34,37 @@ 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, + /* $FlowFixMe[unclear-type] Interceptors annotate the header map with + * differing value types, which an invariant indexer would reject. */ + headers: Object, + ): void, responseReceived( id: number, url: string, status: number, + /* $FlowFixMe[unclear-type] Interceptors annotate the header map with + * differing value types, which an invariant indexer would reject. */ headers: Object, ): void, dataReceived(id: number, data: string): void, @@ -145,7 +167,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 +181,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 +324,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 +355,7 @@ class XMLHttpRequest extends EventTarget { __didReceiveResponse( requestId: number, status: number, - responseHeaders: ?Object, + responseHeaders: ?{[string]: string}, responseURL: ?string, ): void { if (requestId === this._requestId) { @@ -343,19 +366,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 +533,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 +570,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) === false) { // async is default throw new Error('Synchronous http requests are not supported'); } @@ -559,7 +583,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 +640,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 +674,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 +691,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..7975529a48b9 100644 --- a/packages/react-native/Libraries/Network/convertRequestBody.js +++ b/packages/react-native/Libraries/Network/convertRequestBody.js @@ -4,28 +4,57 @@ * 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 isObjectRequestBody( + body: unknown, +): implies body is RequestBodyResult; +function isObjectRequestBody(body: unknown) { + return body != null && typeof body === 'object'; +} + +function convertRequestBody(body: ?RequestBody): ?RequestBodyResult { if (typeof body === 'string') { return {string: body}; } @@ -35,12 +64,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 (isObjectRequestBody(body)) { + return body; + } + return null; } export default convertRequestBody; diff --git a/packages/react-native/Libraries/ReactNative/BridgelessUIManager.js b/packages/react-native/Libraries/ReactNative/BridgelessUIManager.js index 0db6d3415496..2e9811e2cb35 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,24 @@ 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('./RendererProxy'); + 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 +413,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 +422,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 +442,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/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/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/ReactNativeApi.d.ts b/packages/react-native/ReactNativeApi.d.ts index e1654b681c24..6e67b017cc72 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<<3ac371b08ca5c491f5c473f088e609cc>> * * 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, @@ -2213,7 +2215,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 +3275,7 @@ declare type OnAnimationDidFailCallback = () => void declare type OpaqueColorValue = NativeColorValue declare type OptionalFlatListProps = { columnWrapperStyle?: ViewStyleProp - extraData?: any + extraData?: unknown fadingEdgeLength?: | (number | undefined) | { @@ -3300,7 +3302,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 +4088,7 @@ declare type RequestBody = | Blob_default | FormData_default | string - | { - uri: string - } + | URIRequestBody declare type RequiredFlatListProps = { data: Readonly> | undefined } @@ -4418,12 +4418,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 +4548,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 +4558,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 +4584,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 +4604,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 +4625,7 @@ declare interface Spec_2 extends TurboModule { readonly updateView: ( reactTag: number, viewName: string, - props: Object, + props: UnsafeObject, ) => void readonly viewIsDescendantOf: ( reactTag: number, @@ -5391,8 +5391,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 +5406,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 +5547,7 @@ declare type ViewConfig = { readonly validAttributes: AttributeConfiguration } declare type ViewInstance = HostInstance +declare type ViewManagerConfig = UnsafeObject declare interface ViewProps extends Readonly< DirectEventProps & GestureResponderHandlers & @@ -5756,7 +5765,7 @@ export { AlertOptions, // 8a116d2a AlertType, // 5ab91217 AndroidKeyboardEvent, // e03becc8 - Animated, // b73ca27a + Animated, // 8f83ae86 AppConfig, // 35c0ca70 AppRegistry, // eb82174d AppState, // 12012be5 @@ -5808,9 +5817,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 +5905,7 @@ export { NativeSyntheticEvent, // 534aaa92 NativeTouchEvent, // 59b676df NativeUIEvent, // 44ac26ac - Networking, // bbc5be42 + Networking, // 2baeacec OpaqueColorValue, // 25f3fa5b PackagerAsset, // d1c88cf4 PanResponder, // e4df325a @@ -5959,10 +5968,10 @@ 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 @@ -6023,7 +6032,7 @@ export { TransformsStyle, // 65e70f18 TurboModule, // dfe29706 TurboModuleRegistry, // 4ace6db2 - UIManager, // afbcdf05 + UIManager, // 6a18fc05 UTFSequence, // ad625158 Vibration, // 31e4bbf8 View, // 14779d0c 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; } From c2766d08482c79485ad55a6b3138ff90f478ffdb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Rub=C3=A9n=20Norte?= Date: Tue, 8 Sep 2026 10:01:08 -0700 Subject: [PATCH 2/6] Make remaining ReactNative and Core modules Flow strict-local (#57730) Summary: Pull Request resolved: https://github.com/react/react-native/pull/57730 Upgrade the remaining ReactNative and Core modules from `flow` to `flow strict-local`, preserving public type signatures so consumers are unaffected. Genuinely-dynamic internal `any` usages are marked with scoped `$FlowFixMe[unclear-type]` where an accurate type cannot be expressed. Changelog: [Internal] Reviewed By: javache Differential Revision: D113763791 --- .../Libraries/Core/Timers/JSTimers.js | 63 +++++++----- .../Libraries/Core/setUpReactDevTools.js | 16 ++-- .../Libraries/Core/setUpReactRefresh.js | 3 +- .../Libraries/ReactNative/AppContainer.js | 3 +- .../Libraries/ReactNative/AppRegistry.flow.js | 12 ++- .../ReactNativeAttributePayload.js | 95 +++++++++++-------- .../ReactNative/requireNativeComponent.js | 8 +- packages/react-native/ReactNativeApi.d.ts | 14 +-- .../setUpFuseboxReactDevToolsDispatcher.js | 2 +- 9 files changed, 134 insertions(+), 82 deletions(-) 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/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/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/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/ReactNativeApi.d.ts b/packages/react-native/ReactNativeApi.d.ts index 6e67b017cc72..bd17c0cea3d4 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<<3ac371b08ca5c491f5c473f088e609cc>> + * @generated SignedSource<> * * This file was generated by scripts/js-api/build-types/index.js. */ @@ -4222,7 +4222,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, @@ -5743,7 +5745,7 @@ declare function Wrapper_default( $$PARAM_0$$: ModalRefProps & ModalProps, ): React.ReactNode declare type WrapperComponentProvider = ( - appParameters: Object, + appParameters?: AppParameters, appKey?: string, ) => React.ComponentType export { @@ -5767,7 +5769,7 @@ export { AndroidKeyboardEvent, // e03becc8 Animated, // 8f83ae86 AppConfig, // 35c0ca70 - AppRegistry, // eb82174d + AppRegistry, // 8ea6b37b AppState, // 12012be5 AppStateEvent, // 80f034c3 AppStateStatus, // 447e5ef2 @@ -5951,7 +5953,7 @@ export { Role, // af7b889d RootTag, // 3cd10504 RootTagContext, // 38bfc8f6 - RootViewStyleProvider, // a4547094 + RootViewStyleProvider, // a29fccf7 Runnable, // 594dd93a Runnables, // 4367c557 SafeAreaView, // 2a5620cd @@ -6048,7 +6050,7 @@ export { VirtualizedSectionList, // 9fd9cd61 VirtualizedSectionListInstance, // 12b706d5 VirtualizedSectionListProps, // 52c34787 - WrapperComponentProvider, // 9ef54e61 + WrapperComponentProvider, // 9b4247f6 codegenNativeCommands, // 628a7c0a codegenNativeComponent, // 520daa94 findNodeHandle, // 93f80214 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 From 80f6f5b0ea7b019eea1a77e5912f59d1ab78d6c6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Rub=C3=A9n=20Norte?= Date: Tue, 8 Sep 2026 10:01:08 -0700 Subject: [PATCH 3/6] Make remaining Utilities modules Flow strict-local (#57739) Summary: Pull Request resolved: https://github.com/react/react-native/pull/57739 Upgrade `deepDiffer`, `codegenNativeCommands`, `Dimensions`, and `ReactNativeTestTools` from `flow` to `flow strict-local`, preserving both public type signatures and runtime behavior. These modules operate on values of arbitrary shape, so the unavoidable `any` and `Function` usages are marked with scoped `$FlowFixMe[unclear-type]` rather than tightened. `Dimensions.addEventListener` deliberately keeps its original `handler: Function` signature. Flow types object properties invariantly, so any precise payload annotation would reject existing subscribers that annotate the payload with their own object shapes, forcing unrelated call sites to change. The generated API snapshot is therefore unchanged for `Dimensions`. Changelog: [Internal] Reviewed By: javache Differential Revision: D113763789 --- .../Libraries/Utilities/Dimensions.js | 4 +++- .../Utilities/ReactNativeTestTools.js | 9 +++++--- .../Utilities/codegenNativeCommands.js | 22 ++++++++++++++----- .../differ/__tests__/deepDiffer-itest.js | 6 ++--- .../Libraries/Utilities/differ/deepDiffer.js | 10 +++++---- .../Utilities/useWindowDimensions.js | 5 +++-- packages/react-native/ReactNativeApi.d.ts | 6 ++--- .../examples/Dimensions/DimensionsExample.js | 2 +- 8 files changed, 41 insertions(+), 23 deletions(-) diff --git a/packages/react-native/Libraries/Utilities/Dimensions.js b/packages/react-native/Libraries/Utilities/Dimensions.js index 85a6fc5dc494..13458dd737db 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 */ @@ -111,6 +111,8 @@ class Dimensions { */ static addEventListener( type: 'change', + /* $FlowFixMe[unclear-type] Callers annotate the payload with assorted + * object shapes that any precise signature would reject. */ handler: Function, ): EventSubscription { invariant( diff --git a/packages/react-native/Libraries/Utilities/ReactNativeTestTools.js b/packages/react-native/Libraries/Utilities/ReactNativeTestTools.js index a29fed1f0efb..47b6bc950040 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,7 +219,8 @@ 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; + // $FlowFixMe[cannot-write] Deliberately overriding toString on the predicate. + fn.toString = () => message; return fn; } 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 bd17c0cea3d4..b19aca0e7ff8 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<<05c2ae417d791bd248bc677e89f0f307>> * * This file was generated by scripts/js-api/build-types/index.js. */ @@ -1730,7 +1730,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( @@ -6051,7 +6051,7 @@ export { VirtualizedSectionListInstance, // 12b706d5 VirtualizedSectionListProps, // 52c34787 WrapperComponentProvider, // 9b4247f6 - codegenNativeCommands, // 628a7c0a + codegenNativeCommands, // 322f3f4e codegenNativeComponent, // 520daa94 findNodeHandle, // 93f80214 processColor, // 6e877698 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)); From 433aa9bdf1e3aad57837cfe0b3ebd7bc2cbe5787 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Rub=C3=A9n=20Norte?= Date: Tue, 8 Sep 2026 10:01:08 -0700 Subject: [PATCH 4/6] Make Settings modules Flow strict-local (#57736) Summary: Pull Request resolved: https://github.com/react/react-native/pull/57736 Upgrade the Settings modules from `flow` to `flow strict-local`, preserving public type signatures so consumers are unaffected. Values read from the settings store are of arbitrary shape, so those internal `any` usages are marked with scoped `$FlowFixMe[unclear-type]`. Changelog: [Internal] Reviewed By: javache Differential Revision: D113763793 --- .../Libraries/Settings/Settings.ios.js | 109 ++++++++++++------ .../Libraries/Settings/Settings.js | 6 +- .../Libraries/Settings/SettingsFallback.js | 6 +- packages/react-native/ReactNativeApi.d.ts | 8 +- 4 files changed, 81 insertions(+), 48 deletions(-) diff --git a/packages/react-native/Libraries/Settings/Settings.ios.js b/packages/react-native/Libraries/Settings/Settings.ios.js index 293fa9612cad..a8e2ffdc44d7 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,33 @@ import RCTDeviceEventEmitter from '../EventEmitter/RCTDeviceEventEmitter'; import NativeSettingsManager from './NativeSettingsManager'; import invariant from 'invariant'; +type SettingsValues = {[string]: unknown, ...}; +type SettingsCallback = () => void; +type SettingsType = { + _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, +}; +type AssignSettings = ( + target: SettingsValues, + source: SettingsValues, +) => SettingsValues; + +declare function castSetting(value: unknown): T; +function castSetting(value: unknown) { + return value; +} + +const assignSettings: AssignSettings = Object.assign; +const maybeNativeSettingsManager: ?typeof NativeSettingsManager = + NativeSettingsManager; + const subscriptions: Array<{ keys: Array, - callback: ?Function, + callback: ?SettingsCallback, ... }> = []; @@ -25,46 +49,71 @@ const subscriptions: Array<{ * @see https://reactnative.dev/docs/settings * @platform ios */ -const Settings = { - _settings: (NativeSettingsManager && - NativeSettingsManager.getConstants().settings) as any, +function get(this: unknown, key: string): ?T { + const receiver = castSetting(this); + const settings = castSetting(receiver._settings); + return castSetting(settings[key]); +} + +function set(this: unknown, settings: SettingsValues): void { + const receiver = castSetting(this); + receiver._settings = assignSettings( + castSetting(receiver._settings), + settings, + ); + NativeSettingsManager.setValues(settings); +} + +function _sendObservations(this: unknown, body: SettingsValues): void { + const receiver = castSetting(this); + const settings = castSetting(receiver._settings); + Object.keys(body).forEach(key => { + const newValue = body[key]; + const didChange = settings[key] !== newValue; + settings[key] = newValue; + + if (didChange) { + subscriptions.forEach(sub => { + if (sub.keys.indexOf(key) !== -1 && sub.callback) { + sub.callback(); + } + }); + } + }); +} + +const Settings: SettingsType = { + _settings: + maybeNativeSettingsManager != null + ? maybeNativeSettingsManager.getConstants().settings + : maybeNativeSettingsManager, /** * Get the current value for the given key. */ - get(key: string): unknown { - // $FlowFixMe[object-this-reference] - return this._settings[key]; - }, + get, /** * 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); - NativeSettingsManager.setValues(settings); - }, + set, /** * Subscribe to changes for the specified keys. The callback is invoked * 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,23 +126,7 @@ const Settings = { } }, - _sendObservations(body: Object) { - 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; - - if (didChange) { - subscriptions.forEach(sub => { - if (sub.keys.indexOf(key) !== -1 && sub.callback) { - sub.callback(); - } - }); - } - }); - }, + _sendObservations, }; RCTDeviceEventEmitter.addListener( 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/ReactNativeApi.d.ts b/packages/react-native/ReactNativeApi.d.ts index b19aca0e7ff8..9bc56f7da9f4 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<<05c2ae417d791bd248bc677e89f0f307>> + * @generated SignedSource<> * * This file was generated by scripts/js-api/build-types/index.js. */ @@ -381,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 @@ -5977,7 +5977,7 @@ export { SectionListRenderItem, // 715b2086 SectionListRenderItemInfo, // 4a48a922 Separators, // 6a45f7e3 - Settings, // 2be0c61e + Settings, // 40b69683 Share, // e4591b32 ShareAction, // ead1004a ShareActionSheetError, // 55e4f451 From b36f1432ef820a1b7edc05a4b470e3549732f80b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Rub=C3=A9n=20Norte?= Date: Tue, 8 Sep 2026 10:01:08 -0700 Subject: [PATCH 5/6] Make processTransform Flow strict-local (#57734) Summary: Pull Request resolved: https://github.com/react/react-native/pull/57734 Upgrade `processTransform` from `flow` to `flow strict-local`, preserving its public type signature. Mechanical violations were fixed accurately; arbitrary-shape internal values are marked with scoped `$FlowFixMe[unclear-type]`. Changelog: [Internal] Reviewed By: javache Differential Revision: D113763784 --- .../Libraries/StyleSheet/processTransform.js | 51 ++++++++++++------- 1 file changed, 33 insertions(+), 18 deletions(-) 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), ); From c34662d85b4467d56bb7b53b91a47c18453f4d47 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Rub=C3=A9n=20Norte?= Date: Tue, 8 Sep 2026 10:04:59 -0700 Subject: [PATCH 6/6] Make assorted remaining modules Flow strict-local (#57737) Summary: Pull Request resolved: https://github.com/react/react-native/pull/57737 Upgrade the remaining assorted modules (Touchable pooling, ActionSheetIOS, Alert, LogBox highlight, Text native component, PushNotificationIOS) from `flow` to `flow strict-local`, preserving public type signatures so consumers are unaffected. Genuinely-dynamic internal `any` usages are marked with scoped `$FlowFixMe[unclear-type]` where an accurate type cannot be expressed. Changelog: [Internal] Reviewed By: javache Differential Revision: D113763783 --- .../ActionSheetIOS/ActionSheetIOS.js | 5 ++- .../react-native/Libraries/Alert/Alert.js | 40 +++++++++---------- .../Components/Touchable/PooledClass.js | 8 +++- .../Libraries/LogBox/UI/AnsiHighlight.js | 3 +- .../PushNotificationIOS.js | 17 +++++++- .../Libraries/Text/TextNativeComponent.js | 30 +++++++++----- 6 files changed, 68 insertions(+), 35 deletions(-) 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/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/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/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;