A framework for building native applications using React
Convert select component types to interface in TS typegen (#56809)
Summary:
Pull Request resolved: https://github.com/facebook/react-native/pull/56809
#### 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<ItemT> extends VirtualizedListProps<ItemT> {
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<Omit<...>>` 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<T>` 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)
Reviewed By: robhogan
Differential Revision: D104808984
fbshipit-source-id: 69c74fde1180a21c7241be53f010702710672638 A
Alex Hunt committed
db89600b562dda28e9d41028b8b6e63ca94b86a0
Parent: 82536f2
Committed by meta-codesync[bot] <215208954+meta-codesync[bot]@users.noreply.github.com>
on 5/18/2026, 10:39:22 AM