facebook / react-native

A framework for building native applications using React
https://reactnative.dev
MIT License
118.74k stars 24.29k forks source link

The react-native environment calls null as a function if it's passed in the finally block #45745

Open trivikr opened 2 months ago

trivikr commented 2 months ago

Description

If the user code incorrectly passes null to finally block, react-native throws TypeError: null is not a function error.

Neither Node.js or Browser calls a variable in finally if it's incorrectly set to null.

Node.js

$ node
Welcome to Node.js v20.15.1.
Type ".help" for more information.
> const onPress = async () => Promise.resolve('success').finally(null);
undefined
> await onPress()
'success'
> 

Browser

Verified in Browser Console of Google Chrome Version 127.0.6533.73 (Official Build) (arm64)

> const onPress = async () => Promise.resolve('success').finally(null);
undefined
> await onPress()
'success'

Steps to reproduce

Check the README of reproducer

React Native Version

0.74.3

Affected Platforms

Runtime - iOS

Output of npx react-native info

System:
  OS: macOS 14.5
  CPU: (10) arm64 Apple M1 Pro
  Memory: 5.92 GB / 32.00 GB
  Shell:
    version: "5.9"
    path: /bin/zsh
Binaries:
  Node:
    version: 20.15.1
    path: ~/.nvm/versions/node/v20.15.1/bin/node
  Yarn:
    version: 3.6.4
    path: ~/.nvm/versions/node/v20.15.1/bin/yarn
  npm:
    version: 10.7.0
    path: ~/.nvm/versions/node/v20.15.1/bin/npm
  Watchman:
    version: 2024.07.15.00
    path: /opt/homebrew/bin/watchman
Managers:
  CocoaPods:
    version: 1.15.2
    path: /opt/homebrew/bin/pod
SDKs:
  iOS SDK:
    Platforms:
      - DriverKit 23.5
      - iOS 17.5
      - macOS 14.5
      - tvOS 17.5
      - visionOS 1.2
      - watchOS 10.5
  Android SDK:
    API Levels:
      - "31"
      - "33"
      - "34"
      - "35"
    Build Tools:
      - 30.0.3
      - 31.0.0
      - 33.0.0
      - 33.0.1
      - 34.0.0
      - 35.0.0
    System Images:
      - android-35 | Google Play ARM 64 v8a
    Android NDK: Not Found
IDEs:
  Android Studio: 2024.1 AI-241.18034.62.2411.12071903
  Xcode:
    version: 15.4/15F31d
    path: /usr/bin/xcodebuild
Languages:
  Java:
    version: 1.8.0_422
    path: /usr/bin/javac
  Ruby:
    version: 3.3.4
    path: /Users/trivikr/.rvm/rubies/ruby-3.3.4/bin/ruby
npmPackages:
  "@react-native-community/cli": Not Found
  react:
    installed: 18.2.0
    wanted: 18.2.0
  react-native:
    installed: 0.74.3
    wanted: 0.74.3
  react-native-macos: Not Found
npmGlobalPackages:
  "*react-native*": Not Found
Android:
  hermesEnabled: true
  newArchEnabled: false
iOS:
  hermesEnabled: true
  newArchEnabled: false

Stacktrace or Logs

N/A

Reproducer

https://github.com/trivikr/react-native-type-error-null-is-not-a-function to reproduce

Screenshots and Videos

The bug was originally seen in AWS SDK for JavaScript v3 in https://github.com/aws/aws-sdk-js-v3/issues/6269#issuecomment-2252850763, where the error was thrown

In the reproducer app, the error is thrown, but it's shown as a warning in the React Native interface. Screenshots are provided in the README of reproducer

react-native-bot commented 2 months ago
:warning: Add or Reformat Version Info
:information_source: We could not find or parse the version number of React Native in your issue report. Please use the template, and report your version including major, minor, and patch numbers - e.g. 0.70.2
react-native-bot commented 2 months ago
:warning: Add or Reformat Version Info
:information_source: We could not find or parse the version number of React Native in your issue report. Please use the template, and report your version including major, minor, and patch numbers - e.g. 0.70.2
trivikr commented 2 months ago

The issue is reproducible in all supported React Native versions. I updated the version in issue description to 0.74.3 which was used in reproducer.

trivikr commented 2 months ago

The TypeScript types allow null and undefined in finally block https://github.com/microsoft/TypeScript/blob/12ae799eda74aca6a4051f1ebee4d2d0c8d817a2/src/lib/es2018.promise.d.ts#L11

However, the MDN for Promise.prototoype.finally() mentions onFinally to be a function.

The MDN for Promise.prototype.then() states

If it is not a function, it is internally replaced with an identity function ((x) => x) which simply passes the fulfillment value forward.

We suspect that Node.js and browser replicate then() behavior for finally() when null or undefined is passed, so that application does not throw error.

trivikr commented 2 months ago

In TC39 specification, there is a workaround if onFinally is not callable in point 5.