From af135a18728fed225ec1ed169289ffb29bc22b47 Mon Sep 17 00:00:00 2001 From: Alex Hunt Date: Thu, 14 May 2026 08:30:19 -0700 Subject: [PATCH 1/2] Convert select component types to interface in TS typegen (#56809) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Summary: #### Motivation Adds (preserves) compatibility of Nativewind and Expo Web type augmentations with the Strict TypeScript API. - These libraries rely on key React Native component types being defined as `interface`, not `type`, since TS module augmentation only supports `interface` declarations. - Previously, our opt-in generated TypeScript types (Strict TypeScript API) emitted all props types as `type Foo = Readonly<...>` ), matching Flow source. However, this API change vs our manual types (which predominantly used `interface` on props) breaks library compatibility in these cases. - This is very awkward to otherwise solve in user space — so opt for backwards compatibility in our new types. #### Primer TypeScript module augmentation lets a library extend an existing module's types without modifying the source. e.g. In Nativewind ([source](https://www.nativewind.dev/docs/guides/third-party-components)): ```ts declare module 'react-native' { interface ScrollViewProps extends ViewProps, ScrollViewPropsIOS, ScrollViewPropsAndroid, Touchable { contentContainerClassName?: string; indicatorClassName?: string; } interface FlatListProps extends VirtualizedListProps { columnWrapperClassName?: string; } interface ViewProps { className?: string; } } ``` Expo's `react-native-web.d.ts` ([source](https://unpkg.com/expo@54.0.31/types/react-native-web.d.ts)) also follows the same pattern across several component props types. #### Changes Add new `build-types` transform, implementing an opt-in `/** build-types emit-as-interface */` annotation, converting eligible `type` aliases to `interface` declarations. - Changing the Flow source files directly isn't viable — Flow doesn't support `interface extends Omit<>`, and several of these types use `Omit<>` in their composition. The post-transform operates on the TypeScript output where `interface extends Readonly>` is valid. The following emitted types are updated: | Type | Augmented by | Source | |---|---|---| | `ViewProps` | `className?` | [Nativewind](https://www.nativewind.dev/docs/guides/third-party-components), [Expo](https://unpkg.com/expo@54.0.31/types/react-native-web.d.ts) | | `ScrollViewProps` | `contentContainerClassName?`, `indicatorClassName?` | [Nativewind](https://www.nativewind.dev/docs/guides/third-party-components) | | `ImagePropsBase` | `className?` | [Expo](https://unpkg.com/expo@54.0.31/types/react-native-web.d.ts) | | `SwitchProps` | `className?` | [Expo](https://unpkg.com/expo@54.0.31/types/react-native-web.d.ts) | | `TouchableWithoutFeedbackProps` | `className?` | [Expo](https://unpkg.com/expo@54.0.31/types/react-native-web.d.ts) | | `InputAccessoryViewProps` | `className?` | [Expo](https://unpkg.com/expo@54.0.31/types/react-native-web.d.ts) | `FlatListProps` is not included in this PR: its bare intersection (no `Readonly<>` wrapper) has a conflicting `fadingEdgeLength` property across constituent types that cannot be expressed as a single `extends` clause without hitting TS2320. This will be addressed in a follow-up. `VirtualizedListWithoutRenderItemProps` in `react-native/virtualized-lists` is separately owned and not changed here. Changelog: [General][Changed] - **Strict TypeScript API**: Select component props types are now `interface` declarations, enabling module augmentation by libraries like NativeWind and Expo (preserve compatibility) Differential Revision: D104808984 --- .../Components/ScrollView/ScrollView.js | 1 + .../Libraries/Components/Switch/Switch.js | 1 + .../TextInput/InputAccessoryView.js | 1 + .../Touchable/TouchableWithoutFeedback.js | 1 + .../Components/View/ViewPropTypes.js | 1 + .../Libraries/Image/ImageProps.js | 1 + .../react-native/Libraries/Lists/FlatList.js | 5 +- packages/react-native/ReactNativeApi.d.ts | 319 +++++++++--------- .../convertTypeAliasesToInterfaces-test.js | 158 +++++++++ .../convertTypeAliasesToInterfaces.js | 193 +++++++++++ .../js-api/build-types/translateSourceFile.js | 1 + 11 files changed, 527 insertions(+), 155 deletions(-) create mode 100644 scripts/js-api/build-types/transforms/typescript/__tests__/convertTypeAliasesToInterfaces-test.js create mode 100644 scripts/js-api/build-types/transforms/typescript/convertTypeAliasesToInterfaces.js diff --git a/packages/react-native/Libraries/Components/ScrollView/ScrollView.js b/packages/react-native/Libraries/Components/ScrollView/ScrollView.js index 53da6ce07e68..d19eb049ee1f 100644 --- a/packages/react-native/Libraries/Components/ScrollView/ScrollView.js +++ b/packages/react-native/Libraries/Components/ScrollView/ScrollView.js @@ -680,6 +680,7 @@ type ScrollViewBaseProps = Readonly<{ scrollViewRef?: React.RefSetter, }>; +/** @build-types emit-as-interface Nativewind compatibility */ export type ScrollViewProps = Readonly<{ ...Omit, ...ScrollViewPropsIOS, diff --git a/packages/react-native/Libraries/Components/Switch/Switch.js b/packages/react-native/Libraries/Components/Switch/Switch.js index d441435a6c6d..790cf7716ff9 100644 --- a/packages/react-native/Libraries/Components/Switch/Switch.js +++ b/packages/react-native/Libraries/Components/Switch/Switch.js @@ -109,6 +109,7 @@ type SwitchPropsBase = { onValueChange?: ?(value: boolean) => Promise | void, }; +/** @build-types emit-as-interface Expo compatibility */ export type SwitchProps = Readonly<{ ...ViewProps, ...SwitchPropsIOS, diff --git a/packages/react-native/Libraries/Components/TextInput/InputAccessoryView.js b/packages/react-native/Libraries/Components/TextInput/InputAccessoryView.js index 20ec3003d5c5..cf487dd02471 100644 --- a/packages/react-native/Libraries/Components/TextInput/InputAccessoryView.js +++ b/packages/react-native/Libraries/Components/TextInput/InputAccessoryView.js @@ -76,6 +76,7 @@ import * as React from 'react'; * For an example, look at InputAccessoryViewExample.js in RNTester. */ +/** @build-types emit-as-interface Expo compatibility */ export type InputAccessoryViewProps = Readonly<{ +children: React.Node, /** diff --git a/packages/react-native/Libraries/Components/Touchable/TouchableWithoutFeedback.js b/packages/react-native/Libraries/Components/Touchable/TouchableWithoutFeedback.js index adb099c27213..c66b542325c5 100755 --- a/packages/react-native/Libraries/Components/Touchable/TouchableWithoutFeedback.js +++ b/packages/react-native/Libraries/Components/Touchable/TouchableWithoutFeedback.js @@ -36,6 +36,7 @@ export type TouchableWithoutFeedbackPropsAndroid = { touchSoundDisabled?: ?boolean, }; +/** @build-types emit-as-interface Expo compatibility */ export type TouchableWithoutFeedbackProps = Readonly< { children?: ?React.Node, diff --git a/packages/react-native/Libraries/Components/View/ViewPropTypes.js b/packages/react-native/Libraries/Components/View/ViewPropTypes.js index a20d8f246ea5..c2be0823fd34 100644 --- a/packages/react-native/Libraries/Components/View/ViewPropTypes.js +++ b/packages/react-native/Libraries/Components/View/ViewPropTypes.js @@ -508,6 +508,7 @@ type ViewBaseProps = Readonly<{ experimental_accessibilityOrder?: ?Array, }>; +/** @build-types emit-as-interface Nativewind, Expo compatibility */ export type ViewProps = Readonly<{ ...DirectEventProps, ...GestureResponderHandlers, diff --git a/packages/react-native/Libraries/Image/ImageProps.js b/packages/react-native/Libraries/Image/ImageProps.js index 2acab590b8d6..11f8abcc7680 100644 --- a/packages/react-native/Libraries/Image/ImageProps.js +++ b/packages/react-native/Libraries/Image/ImageProps.js @@ -125,6 +125,7 @@ export type ImagePropsAndroid = Readonly<{ resizeMultiplier?: ?number, }>; +/** @build-types emit-as-interface Expo compatibility */ export type ImagePropsBase = Readonly<{ ...Omit, /** diff --git a/packages/react-native/Libraries/Lists/FlatList.js b/packages/react-native/Libraries/Lists/FlatList.js index 3feea7d892d5..43256b3f79db 100644 --- a/packages/react-native/Libraries/Lists/FlatList.js +++ b/packages/react-native/Libraries/Lists/FlatList.js @@ -182,7 +182,8 @@ type FlatListBaseProps = { ...OptionalFlatListProps, }; -export type FlatListProps = { +/** @build-types emit-as-interface Nativewind compatibility */ +export type FlatListProps = Readonly<{ ...Omit< VirtualizedListProps, | 'data' @@ -194,7 +195,7 @@ export type FlatListProps = { >, ...FlatListBaseProps, ... -}; +}>; /** * A performant interface for rendering simple, flat lists, supporting the most handy features: diff --git a/packages/react-native/ReactNativeApi.d.ts b/packages/react-native/ReactNativeApi.d.ts index 8b629454db4c..1b73dfad14c9 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<> * * This file was generated by scripts/js-api/build-types/index.js. */ @@ -2361,16 +2361,19 @@ declare class FlatList extends React.PureComponent< } declare type FlatListBaseProps = RequiredFlatListProps & OptionalFlatListProps -declare type FlatListProps = Omit< - VirtualizedListProps, - | "data" - | "getItem" - | "getItemCount" - | "getItemLayout" - | "keyExtractor" - | "renderItem" -> & - FlatListBaseProps +declare interface FlatListProps + extends Readonly< + Omit< + VirtualizedListProps, + | "data" + | "getItem" + | "getItemCount" + | "getItemLayout" + | "keyExtractor" + | "renderItem" + > & + FlatListBaseProps + > {} declare type flatten = typeof flatten declare type FlattenDepthLimiter = [void, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9] declare function flattenStyle_default< @@ -2609,7 +2612,7 @@ declare type ImageProgressEventIOS = NativeSyntheticEvent< declare type ImageProps = Readonly< ImagePropsIOS & ImagePropsAndroid & - ImagePropsBase & { + Omit & { style?: ImageStyleProp } > @@ -2620,51 +2623,52 @@ declare type ImagePropsAndroid = { readonly resizeMethod?: "auto" | "none" | "resize" | "scale" readonly resizeMultiplier?: number } -declare type ImagePropsBase = Readonly< - Omit< - Omit, - | "accessibilityLabel" - | "accessible" - | "aria-label" - | "aria-labelledby" - | "children" - | "onLayout" - | "testID" - > & { - accessibilityLabel?: string - accessible?: boolean - alt?: string - "aria-label"?: string - "aria-labelledby"?: string - blurRadius?: number - capInsets?: EdgeInsetsProp - children?: never - crossOrigin?: "anonymous" | "use-credentials" - height?: number - internal_analyticTag?: string - onError?: (event: ImageErrorEvent) => void - onLayout?: (event: LayoutChangeEvent) => unknown - onLoad?: (event: ImageLoadEvent) => void - onLoadEnd?: () => void - onLoadStart?: () => void - referrerPolicy?: - | "no-referrer-when-downgrade" - | "no-referrer" - | "origin-when-cross-origin" - | "origin" - | "same-origin" - | "strict-origin-when-cross-origin" - | "strict-origin" - | "unsafe-url" - resizeMode?: ImageResizeMode - source?: ImageSource - src?: string - srcSet?: string - testID?: string - tintColor?: ColorValue - width?: number - } -> +declare interface ImagePropsBase + extends Readonly< + Omit< + Omit, + | "accessibilityLabel" + | "accessible" + | "aria-label" + | "aria-labelledby" + | "children" + | "onLayout" + | "testID" + > + > { + readonly accessibilityLabel?: string + readonly accessible?: boolean + readonly alt?: string + readonly "aria-label"?: string + readonly "aria-labelledby"?: string + readonly blurRadius?: number + readonly capInsets?: EdgeInsetsProp + readonly children?: never + readonly crossOrigin?: "anonymous" | "use-credentials" + readonly height?: number + readonly internal_analyticTag?: string + readonly onError?: (event: ImageErrorEvent) => void + readonly onLayout?: (event: LayoutChangeEvent) => unknown + readonly onLoad?: (event: ImageLoadEvent) => void + readonly onLoadEnd?: () => void + readonly onLoadStart?: () => void + readonly referrerPolicy?: + | "no-referrer-when-downgrade" + | "no-referrer" + | "origin-when-cross-origin" + | "origin" + | "same-origin" + | "strict-origin-when-cross-origin" + | "strict-origin" + | "unsafe-url" + readonly resizeMode?: ImageResizeMode + readonly source?: ImageSource + readonly src?: string + readonly srcSet?: string + readonly testID?: string + readonly tintColor?: ColorValue + readonly width?: number +} declare type ImagePropsIOS = { readonly defaultSource?: ImageSource readonly onPartialLoad?: () => void @@ -2730,7 +2734,7 @@ declare class Info { } declare type InnerViewInstance = React.ComponentRef declare type InputAccessoryView = typeof InputAccessoryView -declare type InputAccessoryViewProps = { +declare interface InputAccessoryViewProps { readonly backgroundColor?: ColorValue readonly children: React.ReactNode readonly nativeID?: string @@ -4500,9 +4504,13 @@ declare interface ScrollViewImperativeMethods { options?: ScrollViewScrollToOptions | undefined, ) => void } -declare type ScrollViewProps = Readonly< - ViewProps & ScrollViewPropsIOS & ScrollViewPropsAndroid & ScrollViewBaseProps -> +declare interface ScrollViewProps + extends Readonly< + ViewProps & + ScrollViewPropsIOS & + ScrollViewPropsAndroid & + ScrollViewBaseProps + > {} declare type ScrollViewPropsAndroid = { readonly endFillColor?: ColorValue readonly fadingEdgeLength?: @@ -5003,9 +5011,8 @@ declare type SwitchNativeProps = Readonly< value?: WithDefault } > -declare type SwitchProps = Readonly< - ViewProps & SwitchPropsIOS & SwitchPropsBase -> +declare interface SwitchProps + extends Readonly {} declare type SwitchPropsBase = { disabled?: boolean ios_backgroundColor?: ColorValue @@ -5569,34 +5576,39 @@ declare type TouchableState = declare function TouchableWithoutFeedback( props: TouchableWithoutFeedbackProps, ): React.ReactNode -declare type TouchableWithoutFeedbackProps = Readonly< - TouchableWithoutFeedbackPropsAndroid & - TouchableWithoutFeedbackPropsIOS & - AccessibilityProps & { - children?: React.ReactNode - delayLongPress?: number - delayPressIn?: number - delayPressOut?: number - disabled?: boolean - focusable?: boolean - hitSlop?: EdgeInsetsOrSizeProp - id?: string - importantForAccessibility?: "auto" | "no-hide-descendants" | "no" | "yes" - nativeID?: string - onAccessibilityAction?: (event: AccessibilityActionEvent) => unknown - onBlur?: (event: BlurEvent) => unknown - onFocus?: (event: FocusEvent) => unknown - onLayout?: (event: LayoutChangeEvent) => unknown - onLongPress?: (event: GestureResponderEvent) => unknown - onPress?: (event: GestureResponderEvent) => unknown - onPressIn?: (event: GestureResponderEvent) => unknown - onPressOut?: (event: GestureResponderEvent) => unknown - pressRetentionOffset?: EdgeInsetsOrSizeProp - rejectResponderTermination?: boolean - style?: ViewStyleProp - testID?: string - } -> +declare interface TouchableWithoutFeedbackProps + extends Readonly< + TouchableWithoutFeedbackPropsAndroid & + TouchableWithoutFeedbackPropsIOS & + AccessibilityProps + > { + readonly children?: React.ReactNode + readonly delayLongPress?: number + readonly delayPressIn?: number + readonly delayPressOut?: number + readonly disabled?: boolean + readonly focusable?: boolean + readonly hitSlop?: EdgeInsetsOrSizeProp + readonly id?: string + readonly importantForAccessibility?: + | "auto" + | "no-hide-descendants" + | "no" + | "yes" + readonly nativeID?: string + readonly onAccessibilityAction?: (event: AccessibilityActionEvent) => unknown + readonly onBlur?: (event: BlurEvent) => unknown + readonly onFocus?: (event: FocusEvent) => unknown + readonly onLayout?: (event: LayoutChangeEvent) => unknown + readonly onLongPress?: (event: GestureResponderEvent) => unknown + readonly onPress?: (event: GestureResponderEvent) => unknown + readonly onPressIn?: (event: GestureResponderEvent) => unknown + readonly onPressOut?: (event: GestureResponderEvent) => unknown + readonly pressRetentionOffset?: EdgeInsetsOrSizeProp + readonly rejectResponderTermination?: boolean + readonly style?: ViewStyleProp + readonly testID?: string +} declare type TouchableWithoutFeedbackPropsAndroid = { touchSoundDisabled?: boolean } @@ -5771,19 +5783,20 @@ declare type ViewConfig = { readonly uiViewClassName: string readonly validAttributes: AttributeConfiguration } -declare type ViewProps = Readonly< - DirectEventProps & - GestureResponderHandlers & - MouseEventProps & - PointerEventProps & - FocusEventProps & - KeyEventProps & - TouchEventProps & - ViewPropsAndroid & - ViewPropsIOS & - AccessibilityProps & - ViewBaseProps -> +declare interface ViewProps + extends Readonly< + DirectEventProps & + GestureResponderHandlers & + MouseEventProps & + PointerEventProps & + FocusEventProps & + KeyEventProps & + TouchEventProps & + ViewPropsAndroid & + ViewPropsIOS & + AccessibilityProps & + ViewBaseProps + > {} declare type ViewPropsAndroid = { readonly focusable?: boolean readonly hasTVPreferredFocus?: boolean @@ -5975,15 +5988,15 @@ export { AccessibilityValue, // cf8bcb74 ActionSheetIOS, // b558559e ActionSheetIOSOptions, // 1756eb5a - ActivityIndicator, // 9b127a18 - ActivityIndicatorProps, // 40142bf3 + ActivityIndicator, // ad37cba0 + ActivityIndicatorProps, // 94db67c0 Alert, // 5bf12165 AlertButton, // bf1a3b60 AlertButtonStyle, // ec9fb242 AlertOptions, // a0cdac0f AlertType, // 5ab91217 AndroidKeyboardEvent, // e03becc8 - Animated, // 29ce3240 + Animated, // 9d76d6c7 AppConfig, // ce4209a7 AppRegistry, // 5edf0524 AppState, // 12012be5 @@ -5995,7 +6008,7 @@ export { BackPressEventName, // 4620fb76 BlurEvent, // e6151a1f BoxShadowValue, // b679703f - Button, // 53167a86 + Button, // 1b481757 ButtonProps, // 0df9cb59 Clipboard, // 41addb89 CodegenTypes, // 0b8108a8 @@ -6014,8 +6027,8 @@ export { DimensionsPayload, // 653bc26c DisplayMetrics, // 1dc35cef DisplayMetricsAndroid, // 872e62eb - DrawerLayoutAndroid, // c0fe33a6 - DrawerLayoutAndroidProps, // 6ce7fb3d + DrawerLayoutAndroid, // 4b041970 + DrawerLayoutAndroidProps, // ffc88788 DrawerSlideEvent, // 0256d35a DropShadowValue, // e9df2606 DynamicColorIOS, // d96c228c @@ -6030,8 +6043,8 @@ export { EventSubscription, // b8d084aa ExtendedExceptionData, // 5a6ccf5a FilterFunction, // bf24c0e3 - FlatList, // 869c5a5f - FlatListProps, // 2a81c428 + FlatList, // 7c3d9ffb + FlatListProps, // cf9bfbb8 FocusEvent, // 62fc1eb8 FontVariant, // 7c7558bb GestureResponderEvent, // f693e9a5 @@ -6043,15 +6056,15 @@ export { IEventEmitter, // fbef6131 IOSKeyboardEvent, // e67bfe3a IgnorePattern, // ec6f6ece - Image, // a2358f8c - ImageBackground, // 414c60ba - ImageBackgroundProps, // 65c127f9 + Image, // 0c1f0f7e + ImageBackground, // 5a23ae3f + ImageBackgroundProps, // 13f111f0 ImageErrorEvent, // d3ee606e ImageLoadEvent, // 6b547ea5 ImageProgressEventIOS, // 4c866a82 - ImageProps, // d917cbd6 + ImageProps, // 4c11aa04 ImagePropsAndroid, // 9fd9bcbb - ImagePropsBase, // c9521ea0 + ImagePropsBase, // 1c26f36a ImagePropsIOS, // c4ae0c06 ImageRequireSource, // 681d683b ImageResolvedAssetSource, // f3060931 @@ -6060,8 +6073,8 @@ export { ImageSourcePropType, // bfb5e5c6 ImageStyle, // ad6a6dee ImageURISource, // 016eb083 - InputAccessoryView, // 2a113ad4 - InputAccessoryViewProps, // 273c1565 + InputAccessoryView, // d664987a + InputAccessoryViewProps, // ac36060b InputModeOptions, // 4e8581b9 Insets, // e7fe432a InteractionManager, // c324d6e3 @@ -6069,8 +6082,8 @@ export { KeyEvent, // 20fa4267 KeyUpEvent, // bc6bd87b Keyboard, // 49414c97 - KeyboardAvoidingView, // 79591758 - KeyboardAvoidingViewProps, // 7cd981a2 + KeyboardAvoidingView, // 0b248a6b + KeyboardAvoidingViewProps, // 199d3c1b KeyboardEvent, // c3f895d4 KeyboardEventEasing, // af4091c8 KeyboardEventName, // 59299ad6 @@ -6095,9 +6108,9 @@ export { MeasureInWindowOnSuccessCallback, // a285f598 MeasureLayoutOnSuccessCallback, // 3592502a MeasureOnSuccessCallback, // 82824e59 - Modal, // dad0b1ce + Modal, // 9d28bb31 ModalBaseProps, // cbd3c10d - ModalProps, // 8e1508c6 + ModalProps, // 7aae1cd8 ModalPropsAndroid, // 515fb173 ModalPropsIOS, // 144bbc95 ModeChangeEvent, // a5e9864f @@ -6135,13 +6148,13 @@ export { PointerEvent, // fe3989a1 PressabilityConfig, // 6dedcb61 PressabilityEventHandlers, // 3e6c0f56 - Pressable, // aef6bb57 + Pressable, // f75f2b8f PressableAndroidRippleConfig, // 42bc9727 - PressableProps, // 3912691c + PressableProps, // d26f4e48 PressableStateCallbackType, // 9af36561 ProcessedColorValue, // 33f74304 - ProgressBarAndroid, // 36757db1 - ProgressBarAndroidProps, // 8bf4dfa6 + ProgressBarAndroid, // 3cb244f9 + ProgressBarAndroidProps, // 304ef6a4 PromiseTask, // 5102c862 PublicRootInstance, // 8040afd7 PublicTextInstance, // cd0d8f8d @@ -6150,8 +6163,8 @@ export { PushNotificationPermissions, // c2e7ae4f Rationale, // 5df1b1c1 ReactNativeVersion, // abd76827 - RefreshControl, // b8659b1f - RefreshControlProps, // e747ed5d + RefreshControl, // f50a1a99 + RefreshControlProps, // ef4eeaff RefreshControlPropsAndroid, // 99f64c97 RefreshControlPropsIOS, // 72a36381 Registry, // 6c39216d @@ -6163,21 +6176,21 @@ export { RootViewStyleProvider, // d4818465 Runnable, // 594dd93a Runnables, // 4367c557 - SafeAreaView, // 9589fa67 + SafeAreaView, // 69d0a921 ScaledSize, // 07e417c7 ScrollEvent, // 5d529218 - ScrollResponderType, // 114c7cc8 + ScrollResponderType, // c6354833 ScrollToLocationParamsType, // d7ecdad1 - ScrollView, // 7180dc9b - ScrollViewImperativeMethods, // 3f95ab77 - ScrollViewProps, // 57e1167c + ScrollView, // ff63e3a5 + ScrollViewImperativeMethods, // 121e0600 + ScrollViewProps, // c04015ea ScrollViewPropsAndroid, // 44210553 ScrollViewPropsIOS, // b34b696c ScrollViewScrollToOptions, // 3313411e SectionBase, // b376bddc - SectionList, // 8ef5401d + SectionList, // 408961c9 SectionListData, // 119baf83 - SectionListProps, // d88cd6f6 + SectionListProps, // 1b4a1372 SectionListRenderItem, // 1fad0435 SectionListRenderItemInfo, // 745e1992 Separators, // 6a45f7e3 @@ -6196,16 +6209,16 @@ export { StyleProp, // fa0e9b4a StyleSheet, // e77dd046 SubmitBehavior, // c4ddf490 - Switch, // 3434138b + Switch, // afcc80b4 SwitchChangeEvent, // 63e9c50b - SwitchProps, // 083b753d + SwitchProps, // 9c7f47a4 Systrace, // b5aa21fc TVViewPropsIOS, // 330ce7b5 TargetedEvent, // 16e98910 TaskProvider, // 266dedf2 Text, // 717d25fe TextContentType, // 239b3ecc - TextInput, // ed3a8375 + TextInput, // e6cad459 TextInputAndroidProps, // 3f09ce49 TextInputChangeEvent, // 3ab11bb4 TextInputContentSizeChangeEvent, // f71f8571 @@ -6213,7 +6226,7 @@ export { TextInputFocusEvent, // 020507e6 TextInputIOSProps, // 0d05a855 TextInputKeyPressEvent, // 3924ad9b - TextInputProps, // 9b370db2 + TextInputProps, // 7b2f6393 TextInputSelectionChangeEvent, // d4d10630 TextInputSubmitEditingEvent, // 22885c31 TextLayoutEvent, // 73ab173e @@ -6221,30 +6234,30 @@ export { TextStyle, // bb9b7a58 ToastAndroid, // 88a8969a Touchable, // da3239ee - TouchableHighlight, // 9d67503a - TouchableHighlightProps, // b2aa6f4b - TouchableNativeFeedback, // 2ed83cf4 - TouchableNativeFeedbackProps, // 1209959b - TouchableOpacity, // fcbaef78 - TouchableOpacityProps, // 13fbd043 - TouchableWithoutFeedback, // 39070327 - TouchableWithoutFeedbackProps, // b847de29 + TouchableHighlight, // bce323c0 + TouchableHighlightProps, // 1d48bf60 + TouchableNativeFeedback, // ae43168e + TouchableNativeFeedbackProps, // 5ac358c3 + TouchableOpacity, // c9c18d36 + TouchableOpacityProps, // 996c242f + TouchableWithoutFeedback, // 71d446ec + TouchableWithoutFeedbackProps, // e0c1c566 TransformsStyle, // 65e70f18 TurboModule, // dfe29706 TurboModuleRegistry, // 4ace6db2 UIManager, // a1a7cc01 UTFSequence, // ad625158 Vibration, // 31e4bbf8 - View, // 02678ca8 - ViewProps, // 15e5f6b9 + View, // c8649954 + ViewProps, // c4a93546 ViewPropsAndroid, // bdfc84a1 ViewPropsIOS, // 58ee19bf ViewStyle, // 00a0f8fb VirtualViewMode, // 6be59722 VirtualizedList, // 68c7345e - VirtualizedListProps, // d414f5ca + VirtualizedListProps, // f9caf7dc VirtualizedSectionList, // 9fd9cd61 - VirtualizedSectionListProps, // 01460821 + VirtualizedSectionListProps, // 3b35070b WrapperComponentProvider, // 9cf3844c codegenNativeCommands, // 628a7c0a codegenNativeComponent, // 2baac257 diff --git a/scripts/js-api/build-types/transforms/typescript/__tests__/convertTypeAliasesToInterfaces-test.js b/scripts/js-api/build-types/transforms/typescript/__tests__/convertTypeAliasesToInterfaces-test.js new file mode 100644 index 000000000000..fe63bb35aa10 --- /dev/null +++ b/scripts/js-api/build-types/transforms/typescript/__tests__/convertTypeAliasesToInterfaces-test.js @@ -0,0 +1,158 @@ +/** + * 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 strict-local + * @format + */ + +const convertTypeAliasesToInterfaces = require('../convertTypeAliasesToInterfaces'); +const babel = require('@babel/core'); + +async function transform(code: string): Promise { + const result = await babel.transformAsync(code, { + plugins: [ + '@babel/plugin-syntax-typescript', + convertTypeAliasesToInterfaces, + ], + }); + + return result?.code ?? ''; +} + +describe('convertTypeAliasesToInterfaces', () => { + test('should not convert type without annotation', async () => { + const result = await transform( + 'declare type ViewProps = Readonly', + ); + expect(result).toBe('declare type ViewProps = Readonly;'); + }); + + test('should convert Readonly intersection of type references', async () => { + const result = await transform( + `/** @build-types emit-as-interface */ +declare type ViewProps = Readonly`, + ); + expect(result).toMatchInlineSnapshot( + `"declare interface ViewProps extends Readonly {}"`, + ); + }); + + test('should convert Readonly intersection with inline object literal', async () => { + const result = await transform( + `/** @build-types emit-as-interface */ +declare type Props = Readonly`, + ); + expect(result).toMatchInlineSnapshot(` +"declare interface Props extends Readonly { + readonly x?: string; + readonly y: number; +}" +`); + }); + + test('should convert Readonly wrapping pure object literal', async () => { + const result = await transform( + `/** @build-types emit-as-interface */ +declare type Props = Readonly<{ a: string; b?: number }>`, + ); + expect(result).toMatchInlineSnapshot(` +"declare interface Props { + readonly a: string; + readonly b?: number; +}" +`); + }); + + test('should convert plain object literal without Readonly', async () => { + const result = await transform( + `/** @build-types emit-as-interface */ +declare type Props = { readonly a: string; readonly b?: number }`, + ); + expect(result).toMatchInlineSnapshot(` +"declare interface Props { + readonly a: string; + readonly b?: number; +}" +`); + }); + + test('should convert intersection without Readonly', async () => { + const result = await transform( + `/** @build-types emit-as-interface */ +declare type Props = A & B`, + ); + expect(result).toMatchInlineSnapshot( + `"declare interface Props extends Readonly {}"`, + ); + }); + + test('should preserve generic type parameters', async () => { + const result = await transform( + `/** @build-types emit-as-interface */ +declare type FlatListProps = Omit & BaseProps`, + ); + expect(result).toMatchInlineSnapshot( + `"declare interface FlatListProps extends Readonly & BaseProps> {}"`, + ); + }); + + test('should handle Readonly wrapping Omit in intersection', async () => { + const result = await transform( + `/** @build-types emit-as-interface */ +declare type Props = Readonly & { alt?: string }>`, + ); + expect(result).toMatchInlineSnapshot(` +"declare interface Props extends Readonly> { + readonly alt?: string; +}" +`); + }); + + test('should preserve surrounding declarations', async () => { + const result = await transform( + `declare type Foo = string; +/** @build-types emit-as-interface */ +declare type Bar = Readonly; +declare type Baz = number;`, + ); + expect(result).toMatchInlineSnapshot(` +"declare type Foo = string; +declare interface Bar extends Readonly {} +declare type Baz = number;" +`); + }); + + test('should convert exported type with annotation on export', async () => { + const result = await transform( + `/** @build-types emit-as-interface */ +export type Props = Readonly`, + ); + expect(result).toMatchInlineSnapshot( + `"export interface Props extends Readonly {}"`, + ); + }); + + test('should throw on unsupported type structure', async () => { + await expect( + transform( + `/** @build-types emit-as-interface */ +declare type Props = string | number`, + ), + ).rejects.toThrow( + "Unsupported type structure for @build-types emit-as-interface on 'Props'", + ); + }); + + test('should handle single type reference', async () => { + const result = await transform( + `/** @build-types emit-as-interface */ +declare type Props = Readonly`, + ); + expect(result).toMatchInlineSnapshot( + `"declare interface Props extends Readonly {}"`, + ); + }); +}); diff --git a/scripts/js-api/build-types/transforms/typescript/convertTypeAliasesToInterfaces.js b/scripts/js-api/build-types/transforms/typescript/convertTypeAliasesToInterfaces.js new file mode 100644 index 000000000000..a9eb7c2375e9 --- /dev/null +++ b/scripts/js-api/build-types/transforms/typescript/convertTypeAliasesToInterfaces.js @@ -0,0 +1,193 @@ +/** + * 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 strict-local + * @format + * @oncall react_native + */ + +import type {PluginObj} from '@babel/core'; + +import * as t from '@babel/types'; + +const ANNOTATION_PATTERN = /@build-types\s+emit-as-interface\b/; + +/** + * Convert `type` aliases annotated with `@build-types emit-as-interface` to + * an `interface` declaration. + * + * Nativewind and Expo/react-native-web rely on TypeScript module augmentation + * to extend props like `className` on React Native component types. This is + * only possible on `interface` declarations (open), not `type` (closed). + */ +function convertToInterface(path: $FlowFixMe): void { + stripAnnotationComments(path); + + const {typeAnnotation} = path.node; + let innerType = typeAnnotation; + let isReadonly = false; + + if ( + t.isTSTypeReference(typeAnnotation) && + t.isIdentifier(typeAnnotation.typeName, {name: 'Readonly'}) && + typeAnnotation.typeParameters?.params.length === 1 + ) { + isReadonly = true; + innerType = typeAnnotation.typeParameters.params[0]; + } + + const extendsClauses: Array = []; + const bodyMembers: Array = []; + + if (t.isTSIntersectionType(innerType)) { + const refMembers: Array = []; + for (const member of innerType.types) { + if (t.isTSTypeLiteral(member)) { + const clonedMembers = t.cloneDeep(member).members; + if (isReadonly) { + makePropertiesReadonly(clonedMembers); + } + bodyMembers.push(...clonedMembers); + } else { + refMembers.push(t.cloneDeep(member)); + } + } + if (refMembers.length > 0) { + const refsType = + refMembers.length === 1 + ? refMembers[0] + : t.tsIntersectionType(refMembers); + if (isReadonly) { + extendsClauses.push( + t.tsExpressionWithTypeArguments( + t.identifier('Readonly'), + t.tsTypeParameterInstantiation([refsType]), + ), + ); + } else { + extendsClauses.push(typeToExtendsClause(refsType, false)); + } + } + } else if (t.isTSTypeLiteral(innerType)) { + const clonedMembers = t.cloneDeep(innerType).members; + if (isReadonly) { + makePropertiesReadonly(clonedMembers); + } + bodyMembers.push(...clonedMembers); + } else if (t.isTSTypeReference(innerType)) { + extendsClauses.push(typeToExtendsClause(innerType, isReadonly)); + } else { + throw new Error( + `Unsupported type structure for @build-types emit-as-interface on '${path.node.id.name}'. Only object literals, type references, and intersections of these are supported.`, + ); + } + + const interfaceNode = t.tsInterfaceDeclaration( + t.cloneDeep(path.node.id), + path.node.typeParameters + ? t.cloneDeep(path.node.typeParameters) + : undefined, + extendsClauses.length > 0 ? extendsClauses : undefined, + t.tsInterfaceBody(bodyMembers), + ); + interfaceNode.declare = path.node.declare ?? false; + + path.replaceWith(interfaceNode); +} + +function hasAnnotationInComments( + comments: ?ReadonlyArray<{type: string, value: string}>, +): boolean { + return ( + Array.isArray(comments) && + comments.some( + comment => + comment.type === 'CommentBlock' && + ANNOTATION_PATTERN.test(comment.value), + ) + ); +} + +function hasEmitAsInterfaceAnnotation(path: $FlowFixMe): boolean { + if (hasAnnotationInComments(path.node.leadingComments)) { + return true; + } + if ( + path.parentPath?.isExportNamedDeclaration() && + hasAnnotationInComments(path.parentPath.node.leadingComments) + ) { + return true; + } + return false; +} + +function typeToExtendsClause( + tsType: t.TSType, + wrapInReadonly: boolean, +): t.TSExpressionWithTypeArguments { + if (wrapInReadonly) { + return t.tsExpressionWithTypeArguments( + t.identifier('Readonly'), + t.tsTypeParameterInstantiation([t.cloneDeep(tsType)]), + ); + } + + if (t.isTSTypeReference(tsType) && t.isIdentifier(tsType.typeName)) { + return t.tsExpressionWithTypeArguments( + t.cloneDeep(tsType.typeName), + tsType.typeParameters ? t.cloneDeep(tsType.typeParameters) : undefined, + ); + } + + return t.tsExpressionWithTypeArguments( + t.identifier('Readonly'), + t.tsTypeParameterInstantiation([t.cloneDeep(tsType)]), + ); +} + +function makePropertiesReadonly(members: Array): void { + for (const member of members) { + if (t.isTSPropertySignature(member)) { + member.readonly = true; + } + } +} + +function stripAnnotationComments(path: $FlowFixMe): void { + const filter = (comments: $FlowFixMe) => + comments?.filter( + (c: $FlowFixMe) => + !(c.type === 'CommentBlock' && ANNOTATION_PATTERN.test(c.value)), + ) ?? []; + path.node.leadingComments = filter(path.node.leadingComments); + if (path.parentPath?.isExportNamedDeclaration()) { + path.parentPath.node.leadingComments = filter( + path.parentPath.node.leadingComments, + ); + } + const target = path.parentPath?.isExportNamedDeclaration() + ? path.parentPath + : path; + const prevSibling = target.getPrevSibling(); + if (prevSibling?.node) { + prevSibling.node.trailingComments = filter( + prevSibling.node.trailingComments, + ); + } +} + +const visitor: PluginObj = { + visitor: { + TSTypeAliasDeclaration(path) { + if (!hasEmitAsInterfaceAnnotation(path)) { + return; + } + convertToInterface(path); + }, + }, +}; + +module.exports = visitor; diff --git a/scripts/js-api/build-types/translateSourceFile.js b/scripts/js-api/build-types/translateSourceFile.js index 1bdb5fc0d4f8..1987f84b00b7 100644 --- a/scripts/js-api/build-types/translateSourceFile.js +++ b/scripts/js-api/build-types/translateSourceFile.js @@ -29,6 +29,7 @@ const preTransforms: Array = [ require('./transforms/flow/ensureNoUnprefixedProps'), ]; const postTransforms = (filePath: string): Array> => [ + require('./transforms/typescript/convertTypeAliasesToInterfaces'), require('./transforms/typescript/replaceDefaultExportName')(filePath), ]; const prettierOptions = {parser: 'babel'}; From 91b955d95c6bf75172cde83f195c0a9c2892654c Mon Sep 17 00:00:00 2001 From: Alex Hunt Date: Thu, 14 May 2026 08:30:19 -0700 Subject: [PATCH 2/2] Extend emit-as-interface coverage for Uniwind compatibility (#56811) Summary: Extends the set of component props types annotated with `build-types emit-as-interface` to cover all types augmented by Uniwind. This is a follow-up to D104808984, which added coverage for NativeWind and Expo. The additional types converted to `interface` declarations are: * `ActivityIndicatorProps` * `ButtonProps` * `ImageBackgroundProps` * `KeyboardAvoidingViewProps` * `ModalBaseProps` * `PressableProps` * `RefreshControlProps` * `SectionListProps` * `TextInputProps` * `TextProps` * `TouchableHighlightProps` See https://github.com/uni-stack/uniwind/blob/a2406b8e04f5597b32a44febe01159c5223ee3c6/packages/uniwind/types.d.ts. Changelog: [General][Changed] - **Strict TypeScript API**: Additional component props types are now `interface` declarations, enabling module augmentation by libraries like Uniwind (preserve compatibility) Differential Revision: D105028014 --- .../ActivityIndicator/ActivityIndicator.js | 1 + .../Libraries/Components/Button.js | 1 + .../Keyboard/KeyboardAvoidingView.js | 1 + .../Components/Pressable/Pressable.js | 1 + .../RefreshControl/RefreshControl.js | 1 + .../Components/TextInput/TextInput.flow.js | 1 + .../Touchable/TouchableHighlight.js | 1 + .../Libraries/Image/ImageProps.js | 1 + .../Libraries/Lists/SectionList.js | 1 + .../react-native/Libraries/Modal/Modal.js | 1 + .../react-native/Libraries/Text/TextProps.js | 2 + packages/react-native/ReactNativeApi.d.ts | 173 +++++++++--------- 12 files changed, 100 insertions(+), 85 deletions(-) diff --git a/packages/react-native/Libraries/Components/ActivityIndicator/ActivityIndicator.js b/packages/react-native/Libraries/Components/ActivityIndicator/ActivityIndicator.js index de86dba65518..28bc1886ee00 100644 --- a/packages/react-native/Libraries/Components/ActivityIndicator/ActivityIndicator.js +++ b/packages/react-native/Libraries/Components/ActivityIndicator/ActivityIndicator.js @@ -34,6 +34,7 @@ type ActivityIndicatorIOSProps = Readonly<{ */ hidesWhenStopped?: ?boolean, }>; +/** @build-types emit-as-interface Uniwind compatibility */ export type ActivityIndicatorProps = Readonly<{ ...ViewProps, ...ActivityIndicatorIOSProps, diff --git a/packages/react-native/Libraries/Components/Button.js b/packages/react-native/Libraries/Components/Button.js index 8e02c93af108..40a926d8ac15 100644 --- a/packages/react-native/Libraries/Components/Button.js +++ b/packages/react-native/Libraries/Components/Button.js @@ -27,6 +27,7 @@ import View from './View/View'; import invariant from 'invariant'; import * as React from 'react'; +/** @build-types emit-as-interface Uniwind compatibility */ export type ButtonProps = Readonly<{ /** Text to display inside the button. On Android the given title will be diff --git a/packages/react-native/Libraries/Components/Keyboard/KeyboardAvoidingView.js b/packages/react-native/Libraries/Components/Keyboard/KeyboardAvoidingView.js index 3c5251f2935b..5a829fc9d457 100644 --- a/packages/react-native/Libraries/Components/Keyboard/KeyboardAvoidingView.js +++ b/packages/react-native/Libraries/Components/Keyboard/KeyboardAvoidingView.js @@ -26,6 +26,7 @@ import Keyboard from './Keyboard'; import * as React from 'react'; import {createRef} from 'react'; +/** @build-types emit-as-interface Uniwind compatibility */ export type KeyboardAvoidingViewProps = Readonly<{ ...ViewProps, diff --git a/packages/react-native/Libraries/Components/Pressable/Pressable.js b/packages/react-native/Libraries/Components/Pressable/Pressable.js index 7fafb836fa89..a1fd57921ee3 100644 --- a/packages/react-native/Libraries/Components/Pressable/Pressable.js +++ b/packages/react-native/Libraries/Components/Pressable/Pressable.js @@ -156,6 +156,7 @@ type PressableBaseProps = Readonly<{ unstable_pressDelay?: ?number, }>; +/** @build-types emit-as-interface Uniwind compatibility */ export type PressableProps = Readonly<{ // Pressability may override `onMouseEnter` and `onMouseLeave` to // implement `onHoverIn` and `onHoverOut` in a platform-agnostic way. diff --git a/packages/react-native/Libraries/Components/RefreshControl/RefreshControl.js b/packages/react-native/Libraries/Components/RefreshControl/RefreshControl.js index 5ce30f3002d8..184fcb9eb3f5 100644 --- a/packages/react-native/Libraries/Components/RefreshControl/RefreshControl.js +++ b/packages/react-native/Libraries/Components/RefreshControl/RefreshControl.js @@ -72,6 +72,7 @@ type RefreshControlBaseProps = Readonly<{ progressViewOffset?: ?number, }>; +/** @build-types emit-as-interface Uniwind compatibility */ export type RefreshControlProps = Readonly<{ ...ViewProps, ...RefreshControlPropsIOS, diff --git a/packages/react-native/Libraries/Components/TextInput/TextInput.flow.js b/packages/react-native/Libraries/Components/TextInput/TextInput.flow.js index 85e6c69b645d..d65f8a2cd864 100644 --- a/packages/react-native/Libraries/Components/TextInput/TextInput.flow.js +++ b/packages/react-native/Libraries/Components/TextInput/TextInput.flow.js @@ -1056,6 +1056,7 @@ type TextInputBaseProps = Readonly<{ textAlign?: ?('left' | 'center' | 'right'), }>; +/** @build-types emit-as-interface Uniwind compatibility */ export type TextInputProps = Readonly<{ ...Omit, ...TextInputIOSProps, diff --git a/packages/react-native/Libraries/Components/Touchable/TouchableHighlight.js b/packages/react-native/Libraries/Components/Touchable/TouchableHighlight.js index 0064e996238a..9dc8b606a0f4 100644 --- a/packages/react-native/Libraries/Components/Touchable/TouchableHighlight.js +++ b/packages/react-native/Libraries/Components/Touchable/TouchableHighlight.js @@ -63,6 +63,7 @@ type TouchableHighlightBaseProps = Readonly<{ hostRef?: React.RefSetter>, }>; +/** @build-types emit-as-interface Uniwind compatibility */ export type TouchableHighlightProps = Readonly<{ ...TouchableWithoutFeedbackProps, ...AndroidProps, diff --git a/packages/react-native/Libraries/Image/ImageProps.js b/packages/react-native/Libraries/Image/ImageProps.js index 11f8abcc7680..3abe9b0dd487 100644 --- a/packages/react-native/Libraries/Image/ImageProps.js +++ b/packages/react-native/Libraries/Image/ImageProps.js @@ -343,6 +343,7 @@ export type ImageProps = Readonly<{ style?: ?ImageStyleProp, }>; +/** @build-types emit-as-interface Uniwind compatibility */ export type ImageBackgroundProps = Readonly<{ ...ImageProps, children?: React.Node, diff --git a/packages/react-native/Libraries/Lists/SectionList.js b/packages/react-native/Libraries/Lists/SectionList.js index 0f1193b9c599..27bd227006b2 100644 --- a/packages/react-native/Libraries/Lists/SectionList.js +++ b/packages/react-native/Libraries/Lists/SectionList.js @@ -105,6 +105,7 @@ type OptionalSectionListProps = { removeClippedSubviews?: boolean, }; +/** @build-types emit-as-interface Uniwind compatibility */ export type SectionListProps = { ...Omit< VirtualizedSectionListProps, diff --git a/packages/react-native/Libraries/Modal/Modal.js b/packages/react-native/Libraries/Modal/Modal.js index 28233e6f266b..80a652ff80c1 100644 --- a/packages/react-native/Libraries/Modal/Modal.js +++ b/packages/react-native/Libraries/Modal/Modal.js @@ -63,6 +63,7 @@ type OrientationChangeEvent = Readonly<{ orientation: 'portrait' | 'landscape', }>; +/** @build-types emit-as-interface Uniwind compatibility */ export type ModalBaseProps = { /** * @deprecated Use animationType instead diff --git a/packages/react-native/Libraries/Text/TextProps.js b/packages/react-native/Libraries/Text/TextProps.js index adbcb59d8ab5..d6be6a5658cb 100644 --- a/packages/react-native/Libraries/Text/TextProps.js +++ b/packages/react-native/Libraries/Text/TextProps.js @@ -267,6 +267,8 @@ type TextBaseProps = Readonly<{ /** * @see https://reactnative.dev/docs/text#reference + * + * @build-types emit-as-interface Uniwind compatibility */ export type TextProps = Readonly<{ ...TextPointerEventProps, diff --git a/packages/react-native/ReactNativeApi.d.ts b/packages/react-native/ReactNativeApi.d.ts index 1b73dfad14c9..91d4f1d00509 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<<67ffb872a8cf879602c9f151a8537b12>> * * This file was generated by scripts/js-api/build-types/index.js. */ @@ -1205,14 +1205,12 @@ declare type ActivityIndicator = typeof ActivityIndicator declare type ActivityIndicatorIOSProps = { readonly hidesWhenStopped?: boolean } -declare type ActivityIndicatorProps = Readonly< - ViewProps & - ActivityIndicatorIOSProps & { - animating?: boolean - color?: ColorValue - size?: IndicatorSize - } -> +declare interface ActivityIndicatorProps + extends Readonly { + readonly animating?: boolean + readonly color?: ColorValue + readonly size?: IndicatorSize +} declare type add = typeof add declare function addChangeListener( listener: (preferences: AppearancePreferences) => void, @@ -1761,7 +1759,7 @@ declare type Builtin = ( ...$$REST$$: ReadonlyArray ) => Date | Error | RegExp | unknown declare type Button = typeof Button -declare type ButtonProps = { +declare interface ButtonProps { readonly accessibilityActions?: ReadonlyArray readonly accessibilityHint?: string readonly accessibilityLabel?: string @@ -2542,14 +2540,13 @@ declare class ImageBackground extends React.Component { render(): React.ReactNode setNativeProps(props: {}): void } -declare type ImageBackgroundProps = Readonly< - Omit & { - children?: React.ReactNode - imageRef?: React.Ref> - imageStyle?: ImageStyleProp - style?: ViewStyleProp - } -> +declare interface ImageBackgroundProps + extends Readonly> { + readonly children?: React.ReactNode + readonly imageRef?: React.Ref> + readonly imageStyle?: ImageStyleProp + readonly style?: ViewStyleProp +} declare type ImageComponentStaticsAndroid = Readonly< ImageComponentStaticsIOS & { abortPrefetch(requestId: number): void @@ -2872,14 +2869,12 @@ declare class KeyboardAvoidingView extends React.Component< constructor(props: KeyboardAvoidingViewProps) render(): React.ReactNode } -declare type KeyboardAvoidingViewProps = Readonly< - ViewProps & { - behavior?: "height" | "padding" | "position" - contentContainerStyle?: ViewStyleProp - enabled?: boolean - keyboardVerticalOffset?: number - } -> +declare interface KeyboardAvoidingViewProps extends Readonly { + readonly behavior?: "height" | "padding" | "position" + readonly contentContainerStyle?: ViewStyleProp + readonly enabled?: boolean + readonly keyboardVerticalOffset?: number +} declare type KeyboardAvoidingViewState = { bottom: number } @@ -3170,7 +3165,7 @@ declare type Message = { }> } declare type Modal = typeof Modal -declare type ModalBaseProps = { +declare interface ModalBaseProps { animated?: boolean animationType?: "fade" | "none" | "slide" backdropColor?: ColorValue @@ -3841,9 +3836,10 @@ declare type PressableBaseProps = { readonly testID?: string readonly testOnly_pressed?: boolean } -declare type PressableProps = Readonly< - Omit & PressableBaseProps -> +declare interface PressableProps + extends Readonly< + Omit & PressableBaseProps + > {} declare type PressableStateCallbackType = { readonly pressed: boolean } @@ -4175,12 +4171,13 @@ declare type RefreshControlBaseProps = { readonly progressViewOffset?: number readonly refreshing: boolean } -declare type RefreshControlProps = Readonly< - ViewProps & - RefreshControlPropsIOS & - RefreshControlPropsAndroid & - RefreshControlBaseProps -> +declare interface RefreshControlProps + extends Readonly< + ViewProps & + RefreshControlPropsIOS & + RefreshControlPropsAndroid & + RefreshControlBaseProps + > {} declare type RefreshControlPropsAndroid = { readonly colors?: ReadonlyArray readonly enabled?: boolean @@ -4611,12 +4608,15 @@ declare type SectionListData< | (Readonly> & SectionT) | (SectionBase & SectionT) | SectionT -declare type SectionListProps = Omit< - VirtualizedSectionListProps, - "getItem" | "getItemCount" | "keyExtractor" | "renderItem" -> & - RequiredSectionListProps & - OptionalSectionListProps +declare interface SectionListProps + extends Readonly< + Omit< + VirtualizedSectionListProps, + "getItem" | "getItemCount" | "keyExtractor" | "renderItem" + > & + RequiredSectionListProps & + OptionalSectionListProps + > {} declare type SectionListRenderItem = ( info: SectionListRenderItemInfo, ) => null | React.ReactNode @@ -5360,12 +5360,13 @@ declare type TextInputKeyPressEventData = Readonly< target?: number } > -declare type TextInputProps = Readonly< - Omit & - TextInputIOSProps & - TextInputAndroidProps & - TextInputBaseProps -> +declare interface TextInputProps + extends Readonly< + Omit & + TextInputIOSProps & + TextInputAndroidProps & + TextInputBaseProps + > {} declare type TextInputSelectionChangeEvent = NativeSyntheticEvent declare type TextInputSelectionChangeEventData = Readonly< @@ -5400,13 +5401,14 @@ declare type TextPointerEventProps = { readonly onPointerLeave?: (event: PointerEvent) => void readonly onPointerMove?: (event: PointerEvent) => void } -declare type TextProps = Readonly< - TextPointerEventProps & - TextPropsIOS & - TextPropsAndroid & - TextBaseProps & - AccessibilityProps -> +declare interface TextProps + extends Readonly< + TextPointerEventProps & + TextPropsIOS & + TextPropsAndroid & + TextBaseProps & + AccessibilityProps + > {} declare type TextPropsAndroid = { adjustsFontSizeToFit?: boolean dataDetectorType?: "all" | "email" | "link" | "none" | "phoneNumber" @@ -5472,12 +5474,13 @@ declare type TouchableHighlightBaseProps = { readonly testOnly_pressed?: boolean readonly underlayColor?: ColorValue } -declare type TouchableHighlightProps = Readonly< - TouchableWithoutFeedbackProps & - AndroidProps & - IOSProps & - TouchableHighlightBaseProps -> +declare interface TouchableHighlightProps + extends Readonly< + TouchableWithoutFeedbackProps & + AndroidProps & + IOSProps & + TouchableHighlightBaseProps + > {} declare class TouchableNativeFeedback extends React.Component< TouchableNativeFeedbackProps, TouchableNativeFeedbackState @@ -5988,15 +5991,15 @@ export { AccessibilityValue, // cf8bcb74 ActionSheetIOS, // b558559e ActionSheetIOSOptions, // 1756eb5a - ActivityIndicator, // ad37cba0 - ActivityIndicatorProps, // 94db67c0 + ActivityIndicator, // 4c70eaf2 + ActivityIndicatorProps, // 91124d11 Alert, // 5bf12165 AlertButton, // bf1a3b60 AlertButtonStyle, // ec9fb242 AlertOptions, // a0cdac0f AlertType, // 5ab91217 AndroidKeyboardEvent, // e03becc8 - Animated, // 9d76d6c7 + Animated, // 56c2a954 AppConfig, // ce4209a7 AppRegistry, // 5edf0524 AppState, // 12012be5 @@ -6008,8 +6011,8 @@ export { BackPressEventName, // 4620fb76 BlurEvent, // e6151a1f BoxShadowValue, // b679703f - Button, // 1b481757 - ButtonProps, // 0df9cb59 + Button, // db5bc4e7 + ButtonProps, // 349967e6 Clipboard, // 41addb89 CodegenTypes, // 0b8108a8 ColorSchemeName, // 6615edd6 @@ -6057,8 +6060,8 @@ export { IOSKeyboardEvent, // e67bfe3a IgnorePattern, // ec6f6ece Image, // 0c1f0f7e - ImageBackground, // 5a23ae3f - ImageBackgroundProps, // 13f111f0 + ImageBackground, // 925c7d7c + ImageBackgroundProps, // db8a916e ImageErrorEvent, // d3ee606e ImageLoadEvent, // 6b547ea5 ImageProgressEventIOS, // 4c866a82 @@ -6082,8 +6085,8 @@ export { KeyEvent, // 20fa4267 KeyUpEvent, // bc6bd87b Keyboard, // 49414c97 - KeyboardAvoidingView, // 0b248a6b - KeyboardAvoidingViewProps, // 199d3c1b + KeyboardAvoidingView, // 77ca6ac1 + KeyboardAvoidingViewProps, // feeaf1de KeyboardEvent, // c3f895d4 KeyboardEventEasing, // af4091c8 KeyboardEventName, // 59299ad6 @@ -6108,9 +6111,9 @@ export { MeasureInWindowOnSuccessCallback, // a285f598 MeasureLayoutOnSuccessCallback, // 3592502a MeasureOnSuccessCallback, // 82824e59 - Modal, // 9d28bb31 - ModalBaseProps, // cbd3c10d - ModalProps, // 7aae1cd8 + Modal, // 773dd8c8 + ModalBaseProps, // 3cca97dc + ModalProps, // 15affa2f ModalPropsAndroid, // 515fb173 ModalPropsIOS, // 144bbc95 ModeChangeEvent, // a5e9864f @@ -6148,9 +6151,9 @@ export { PointerEvent, // fe3989a1 PressabilityConfig, // 6dedcb61 PressabilityEventHandlers, // 3e6c0f56 - Pressable, // f75f2b8f + Pressable, // b3ff5f4d PressableAndroidRippleConfig, // 42bc9727 - PressableProps, // d26f4e48 + PressableProps, // be9ff61e PressableStateCallbackType, // 9af36561 ProcessedColorValue, // 33f74304 ProgressBarAndroid, // 3cb244f9 @@ -6163,8 +6166,8 @@ export { PushNotificationPermissions, // c2e7ae4f Rationale, // 5df1b1c1 ReactNativeVersion, // abd76827 - RefreshControl, // f50a1a99 - RefreshControlProps, // ef4eeaff + RefreshControl, // 5aebc527 + RefreshControlProps, // 476c709f RefreshControlPropsAndroid, // 99f64c97 RefreshControlPropsIOS, // 72a36381 Registry, // 6c39216d @@ -6188,9 +6191,9 @@ export { ScrollViewPropsIOS, // b34b696c ScrollViewScrollToOptions, // 3313411e SectionBase, // b376bddc - SectionList, // 408961c9 + SectionList, // d473b52b SectionListData, // 119baf83 - SectionListProps, // 1b4a1372 + SectionListProps, // e09d32da SectionListRenderItem, // 1fad0435 SectionListRenderItemInfo, // 745e1992 Separators, // 6a45f7e3 @@ -6216,9 +6219,9 @@ export { TVViewPropsIOS, // 330ce7b5 TargetedEvent, // 16e98910 TaskProvider, // 266dedf2 - Text, // 717d25fe + Text, // 608149e8 TextContentType, // 239b3ecc - TextInput, // e6cad459 + TextInput, // 8f03c64b TextInputAndroidProps, // 3f09ce49 TextInputChangeEvent, // 3ab11bb4 TextInputContentSizeChangeEvent, // f71f8571 @@ -6226,16 +6229,16 @@ export { TextInputFocusEvent, // 020507e6 TextInputIOSProps, // 0d05a855 TextInputKeyPressEvent, // 3924ad9b - TextInputProps, // 7b2f6393 + TextInputProps, // b2b2cc01 TextInputSelectionChangeEvent, // d4d10630 TextInputSubmitEditingEvent, // 22885c31 TextLayoutEvent, // 73ab173e - TextProps, // 68a1c0e8 + TextProps, // 4c29419c TextStyle, // bb9b7a58 ToastAndroid, // 88a8969a Touchable, // da3239ee - TouchableHighlight, // bce323c0 - TouchableHighlightProps, // 1d48bf60 + TouchableHighlight, // 07ade135 + TouchableHighlightProps, // b9ceb181 TouchableNativeFeedback, // ae43168e TouchableNativeFeedbackProps, // 5ac358c3 TouchableOpacity, // c9c18d36