Files
react-native/scripts/js-api
Alex Hunt db89600b56 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
2026-05-18 03:39:22 -07:00
..

scripts/js-api

TypeScript build pipeline for React Native's JavaScript API.

Overview

yarn build-types is a custom build pipeline for translating React Native's Flow source code to TypeScript.

Specifically, it reduces the runtime JavaScript API of react-native into two outputs:

  • Generated TypeScript types
    Public user types for react-native, shipped to npm
    packages/react-native/types_generated/
  • ‌Public API snapshot
    Snapshot file of the public API shape, used by maintainers
    packages/react-native/ReactNativeApi.d.ts

Dependencies

yarn build-types makes use of the following dependencies, composed with other pre/post transformation steps and dependency resolution.

Usage

Build generated types + API snapshot

Maintainers should run this script whenever making intentional API changes.

# Build types + API snapshot
yarn build-types [--validate]

# Build types without API snapshot
yarn build-types --skip-snapshot

Diff API snapshot compatibility

This script is run by CI to compare changes to ReactNativeApi.d.ts between commits.

# Compare two versions of the API snapshot
yarn js-api-diff <before.d.ts> <after.d.ts>
{
  "result": "BREAKING",
  "changedApis": [
    "ViewStyle"
  ]
}

Configuration

Sparse configuration options are defined and documented in scripts/js-api/config.js.

About the two output formats

Generated TypeScript types

types_generated/

Directory providing TypeScript user types for the react-native package, distributed via npm.

  • Gitignored.
  • Scoped to the index.d.ts entry point via package.json#exports.
  • Preserves unstable_ and experimental_ APIs.
  • Preserves doc comments.
  • Preserves source file names (for go to definition).

Public API snapshot

ReactNativeApi.d.ts

Provides a human-readable, maintainable reference of the React Native's public JavaScript API, optimized for developers and diff tooling.

  • Committed to the repo.
  • Strips unstable_ and experimental_ APIs.
  • Strips doc comments.
  • Strips source file names (types are merged into a single program).
  • Versions exported APIs with an 8 char SHA hash, which will be updated when any input type dependencies change shape.