Skip to content

Codex Review Findings #845

@cknitt

Description

@cknitt

Inspired by the great findings Codex had in #844, I asked it to review the complete rescript-react-native codebase against docs and TypeScript typedefs.

Below are the unedited findings. Not all will be valid/actionable, as oftentimes the bindings choose stricter typings intentionally for good reason.

Review Findings

Reviewed against React Native 0.81.0 TypeScript definitions, which matches this package's declared peer dependency. This document expands the initial spot review into a broader public-surface audit of the bindings under src/apis, src/components, and src/elements.

Findings

  1. [P1] Bindings still target root exports that React Native 0.81 does not publicly export anymore: YellowBox.res, SoundManager.res, and ViewPagerAndroidMethods.res. In react-native@0.81.0, types/index.d.ts and index.js expose none of YellowBox, SoundManager, or ViewPagerAndroid, so these bindings can resolve missing properties at runtime.

  2. [P1] The package is missing a substantial set of public React Native 0.81 exports entirely. The RN export list includes modules such as I18nManager, RCTDeviceEventEmitter, RCTNativeAppEventEmitter, registerCallableModule, requireNativeComponent, Systrace, PushNotificationIOS, TurboModuleRegistry, codegenNativeCommands, codegenNativeComponent, ErrorUtils, RootTag, LayoutConformance, and DevMenu, but there are no corresponding bindings under src/apis or src/components. This is a package-level completeness gap against RN 0.81’s public surface.

  3. [P1] Linking models async APIs as sync or opaque values in Linking.res. openSettings is promise<'a> instead of Promise<void>, and both sendIntent overloads return unit, while React Native 0.81 types them as Promise<void>. That prevents correct awaiting and error handling for a public API.

  4. [P1] Image has multiple static API mismatches in Image.res. resizeMethod is missing 'none'; loadingIndicatorSource is typed as array<Source.t> instead of a single source; prefetch returns a request id instead of Promise<bool>; queryCache returns unit instead of a promise of cache-state map; and the promise overloads for getSize, plus getSizeWithHeaders and prefetchWithMetadata, are missing entirely.

  5. [P1] SectionList’s section shape is not compatible with React Native’s public typing in VirtualizedSectionList.res. React Native defines each section as SectionBase & SectionT, so custom fields live directly on section; this binding forces them under sectionData?. Standard RN usage like section.title in headers will not type-check naturally here.

  6. [P1] The exported top-level Animated.timing, Animated.spring, and Animated.decay are aliased from Value.* at Animated.res, so they only accept Animated.Value. React Native 0.81 types them for Animated.Value | Animated.ValueXY, and spring also supports richer toValue shapes including colors and animated values. Valid RN animation calls are rejected by this binding surface.

  7. [P1] VirtualizedList mis-types the onStartReached callback payload in VirtualizedList.res. The binding uses {distanceFromEnd: float} for both start and end callbacks, while RN 0.81 uses {distanceFromStart: number} for onStartReached. Consumers matching on the field name cannot write the documented RN callback shape.

  8. [P2] AppRegistry has several wrong signatures in AppRegistry.res. registerComponent returns unit instead of string, unmountApplicationComponentAtRootTag takes string instead of number, and public 0.81 APIs such as setRootViewStyleProvider, setSurfaceProps, and setComponentProviderInstrumentationHook are missing.

  9. [P2] Pressable is materially narrower than RN in Pressable.res. children only accepts the render-prop form, style only accepts the callback form, hitSlop and pressRetentionOffset cannot be numbers, and props like cancelable, delayHoverIn, and delayHoverOut are absent.

  10. [P2] StatusBar is incomplete and over-strict in StatusBar.res. setHidden, setBarStyle, and setBackgroundColor require their second parameter even though RN marks it optional, and the stack-entry APIs pushStackEntry, popStackEntry, and replaceStackEntry are missing.

  11. [P2] NativeEventEmitter loses part of the actual API shape in NativeEventEmitter.res. The constructor is required here but optional in RN types, listenerCount is typed as returning unit instead of number, and addListener omits the optional context parameter.

  12. [P2] AccessibilityInfo is behind the current RN surface in AccessibilityInfo.res. The binding misses isHighTextContrastEnabled, isDarkerSystemColorsEnabled, announceForAccessibilityWithOptions, and sendAccessibilityEvent, and its event union omits change events such as change, highTextContrastChanged, and darkerSystemColorsChanged.

  13. [P2] AppState under-models the current API in AppState.res. RN 0.81 exposes isAvailable, but the binding does not. The addEventListener binding also treats all event kinds as if they share one callback shape, while the TS surface models the API around AppStateEvent and NativeEventSubscription.

  14. [P2] Appearance has both type and event-shape drift in Appearance.res. setColorScheme is narrower than RN because it does not allow null/undefined, and addChangeListener incorrectly takes unit => unit instead of a callback receiving {colorScheme} preferences.

  15. [P2] BackHandler still exposes an obsolete API surface in BackHandler.res. RN 0.81 types only the 'hardwareBackPress' event and no longer includes removeEventListener, but this binding still exports both an extra #backPress event case and removeEventListener.

  16. [P2] Dimensions is missing the public set API in Dimensions.res. RN 0.81 exposes Dimensions.set(dims) for native-driven updates, but the binding omits it entirely.

  17. [P2] Keyboard is missing public methods from RN 0.81 in Keyboard.res. scheduleLayoutAnimation and metrics() are absent. That leaves part of the documented keyboard coordination API unavailable.

  18. [P2] LayoutAnimation is incomplete in LayoutAnimation.res. configureNext lacks the optional failure callback, and RN’s exported Types, Properties, and configChecker members are missing. The convenience methods easeInEaseOut, linear, and spring also omit their optional completion callback.

  19. [P2] Share has an incorrect overload in Share.res. RN 0.81 exposes a single share(content, options?) => Promise<ShareAction>, while this binding splits out shareWithOptions returning promise<bool>, which does not match the public contract.

  20. [P2] Platform is dramatically incomplete in Platform.res. The binding exposes only Platform.OS, but RN 0.81 also exposes Version, constants, isTV, isTesting, and select. It also omits the 'native' discriminant present in PlatformOSType.

  21. [P2] PermissionsAndroid is stale in PermissionsAndroid.res. The permission constants are missing READ_MEDIA_VISUAL_USER_SELECTED, and the deprecated-but-still-typed checkPermission and requestPermission APIs are absent from the public binding surface.

  22. [P2] UIManager only exposes a small subset of RN’s public API in UIManager.res. Public methods such as measure, measureInWindow, measureLayout, getViewManagerConfig, hasViewManagerConfig, and dispatchViewManagerCommand are missing.

  23. [P2] ActionSheetIOS is narrower than RN in ActionSheetIOS.res. destructiveButtonIndex should accept both number and number[]; disabledButtonIndices should be an array; and public options such as anchor, userInterfaceStyle, and disabledButtonTintColor are missing.

  24. [P2] Alert is missing the userInterfaceStyle option in Alert.res. RN 0.81 exposes it on AlertOptions, but this binding does not.

  25. [P2] LogBox is too narrow in LogBox.res. ignoreLogs only accepts array<string> but RN accepts (string | RegExp)[]; ignoreAllLogs omits its optional boolean parameter; and the public install and uninstall methods are missing.

  26. [P2] DevSettings should expose a single optional-argument reload(reason?) API, not two separate bindings in DevSettings.res. RN 0.81 also models DevSettings as extending NativeEventEmitter, which this binding does not surface.

  27. [P2] Settings is narrower than RN in Settings.res. get is typed as returning string instead of any, watchKeys does not support the single-string overload, and watchToken is opaque instead of the numeric watch id RN returns.

  28. [P2] Button does not match RN’s required prop shape in Button.res. title is optional locally but required in RN 0.81. That weakens a key required prop from the public API.

  29. [P2] Switch is missing public props in Switch.res. RN 0.81 includes onChange plus deprecated compatibility props onTintColor, thumbTintColor, and tintColor, but the binding does not.

  30. [P2] TouchableWithoutFeedback is narrower than RN in TouchableWithoutFeedback.res. children is limited to React.element instead of ReactNode; hitSlop and pressRetentionOffset cannot be numbers; and props such as rejectResponderTermination, focusable, id, and style are missing.

  31. [P2] TouchableOpacity diverges from RN in TouchableOpacity.res. It is missing the TV/Android navigation props nextFocusDown, nextFocusForward, nextFocusLeft, nextFocusRight, and nextFocusUp, and it exposes focusedOpacity, which is not part of the RN 0.81 TypeScript surface.

  32. [P2] TouchableNativeFeedback under-models its background API in TouchableNativeFeedback.res. Ripple is missing the optional rippleRadius parameter, the selectable background helpers are missing their optional radius parameter, and the background shape is opaque instead of reflecting the discriminated objects RN exposes.

  33. [P2] InputAccessoryView is narrower than RN in InputAccessoryView.res. children is typed as React.element instead of ReactNode.

  34. [P2] Modal is incomplete in Modal.res. RN 0.81’s ModalProps include ViewProps, but this binding does not spread them at all. It also misses allowSwipeDismissal, narrows children to React.element, and uses narrower event callback signatures for onShow and onRequestClose.

  35. [P2] RefreshControl has a shape mismatch in RefreshControl.res. RN 0.81 types size as a numeric constant-backed value and exposes static RefreshControl.SIZE, but the binding replaces that with a closed polymorphic variant and omits the static.

  36. [P2] DrawerLayoutAndroid has a couple of public-contract mismatches in DrawerLayoutAndroid.res. renderNavigationView is required in RN but optional here, and onDrawerStateChanged is typed with lowercase states even though the RN typings use 'Idle' | 'Dragging' | 'Settling'.

  37. [P2] Text is materially incomplete in Text.res. RN 0.81 includes props such as dynamicTypeRamp, lineBreakMode, id, and pointerEvents, but the binding omits them. It also models Android data detection as dataDetectorTypes?: array<dataDetectorType> instead of RN’s singular dataDetectorType prop and current value set.

  38. [P2] View is missing several current public props in View.res. RN 0.81 includes id, focusable, tabIndex, Android onBlur/onFocus, TV parallax props, and collapsableChildren, but the binding omits them. children is also narrowed to React.element instead of ReactNode, and hitSlop cannot be a number.

  39. [P2] TextInput is behind RN 0.81 in TextInput.res. Public props such as disableKeyboardShortcuts, passwordRules, selectionState, cursorColor, selectionHandleColor, lineBreakModeIOS, and smartInsertDelete are missing. The textContentType union is stale and omits newer iOS values like creditCardExpiration, birthdate, and cellularIMEI, and dataDetectorTypes is modeled only as an array even though RN also accepts a single value.

  40. [P2] ImageBackground narrows its callback prop in ImageBackground.res. RN exposes imageRef?(image: Image): void, but the binding models it as imageRef?: Image.ref, which is not the same API shape.

  41. [P2] ScrollView is narrower than RN in ScrollView.res. keyboardShouldPersistTaps omits the legacy boolean forms RN still types; StickyHeaderComponent is typed as a rendered element instead of the component/element union RN uses elsewhere; and many callback/render prop positions are narrower than the RN TS surface.

  42. [P2] VirtualizedList is materially narrower than RN in VirtualizedList.res. renderItem does not expose the full separators API (updateProps is missing), list header/footer/empty components are narrowed to unit => React.element instead of component/element unions, renderScrollComponent omits its props parameter, and keyExtractor is required even though RN makes it optional.

  43. [P2] FlatList inherits the VirtualizedList narrowing and also misses public props RN 0.81 types directly in FlatList.res, including legacyImplementation and Android’s richer fadingEdgeLength shape.

  44. [P2] Animated constructors and helpers are still behind the RN 0.81 typings in Animated.res. Value.create, ValueXY.create, and Color.create omit the optional config parameter; ValueXY.create cannot accept animated axes; parallel and event require config objects RN makes optional; createAnimatedComponent omits its optional options; and animated nodes do not expose hasListeners().

Notes

  • Deprecated but still-exported RN 0.81 APIs such as Clipboard, ProgressBarAndroid, and SafeAreaView are present in this package. I did not count deprecation by itself as a finding when the binding shape still tracks RN’s public surface closely enough.
  • Helper modules such as Color, Style, Event, EventSubscription, and Packager were only checked insofar as they affect the exposed RN bindings. This review is focused on the public React Native-facing contract of the package.

Sources

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions