diff --git a/Libraries/Components/ActivityIndicatorIOS/ActivityIndicatorIOS.ios.js b/Libraries/Components/ActivityIndicatorIOS/ActivityIndicatorIOS.ios.js index b673258722..f2bcbfd516 100644 --- a/Libraries/Components/ActivityIndicatorIOS/ActivityIndicatorIOS.ios.js +++ b/Libraries/Components/ActivityIndicatorIOS/ActivityIndicatorIOS.ios.js @@ -7,6 +7,7 @@ * of patent rights can be found in the PATENTS file in the same directory. * * @providesModule ActivityIndicatorIOS + * @flow */ 'use strict'; @@ -29,6 +30,12 @@ var SpinnerSize = keyMirror({ var GRAY = '#999999'; +type DefaultProps = { + animating: boolean; + size: 'small' | 'large'; + color: string; +}; + var ActivityIndicatorIOS = React.createClass({ mixins: [NativeMethodsMixin], @@ -51,7 +58,7 @@ var ActivityIndicatorIOS = React.createClass({ ]), }, - getDefaultProps: function() { + getDefaultProps: function(): DefaultProps { return { animating: true, size: SpinnerSize.small, diff --git a/Libraries/Components/DatePicker/DatePickerIOS.ios.js b/Libraries/Components/DatePicker/DatePickerIOS.ios.js index 7be126675d..9bd0a2ac47 100644 --- a/Libraries/Components/DatePicker/DatePickerIOS.ios.js +++ b/Libraries/Components/DatePicker/DatePickerIOS.ios.js @@ -7,6 +7,7 @@ * of patent rights can be found in the PATENTS file in the same directory. * * @providesModule DatePickerIOS + * @flow * * This is a controlled component version of RCTDatePickerIOS */ @@ -26,6 +27,12 @@ var merge = require('merge'); var DATEPICKER = 'datepicker'; +type DefaultProps = { + mode: 'date' | 'time' | 'datetime'; +}; + +type Event = Object; + /** * Use `DatePickerIOS` to render a date/time picker (selector) on iOS. This is * a controlled component, so you must hook in to the `onDateChange` callback @@ -85,13 +92,13 @@ var DatePickerIOS = React.createClass({ timeZoneOffsetInMinutes: PropTypes.number, }, - getDefaultProps: function() { + getDefaultProps: function(): DefaultProps { return { mode: 'datetime', }; }, - _onChange: function(event) { + _onChange: function(event: Event) { var nativeTimeStamp = event.nativeEvent.timestamp; this.props.onDateChange && this.props.onDateChange( new Date(nativeTimeStamp) diff --git a/Libraries/Components/MapView/MapView.js b/Libraries/Components/MapView/MapView.js index b2409a05b9..b778fc695b 100644 --- a/Libraries/Components/MapView/MapView.js +++ b/Libraries/Components/MapView/MapView.js @@ -7,6 +7,7 @@ * of patent rights can be found in the PATENTS file in the same directory. * * @providesModule MapView + * @flow */ 'use strict'; @@ -21,6 +22,8 @@ var deepDiffer = require('deepDiffer'); var insetsDiffer = require('insetsDiffer'); var merge = require('merge'); +type Event = Object; + var MapView = React.createClass({ mixins: [NativeMethodsMixin], @@ -119,7 +122,7 @@ var MapView = React.createClass({ onRegionChangeComplete: React.PropTypes.func, }, - _onChange: function(event) { + _onChange: function(event: Event) { if (event.nativeEvent.continuous) { this.props.onRegionChange && this.props.onRegionChange(event.nativeEvent.region); diff --git a/Libraries/Components/Navigation/NavigatorIOS.ios.js b/Libraries/Components/Navigation/NavigatorIOS.ios.js index 06c353592d..aa5039edbf 100644 --- a/Libraries/Components/Navigation/NavigatorIOS.ios.js +++ b/Libraries/Components/Navigation/NavigatorIOS.ios.js @@ -7,6 +7,7 @@ * of patent rights can be found in the PATENTS file in the same directory. * * @providesModule NavigatorIOS + * @flow */ 'use strict'; @@ -59,7 +60,7 @@ var RCTNavigatorItem = createReactIOSNativeComponentClass({ var NavigatorTransitionerIOS = React.createClass({ requestSchedulingNavigation: function(cb) { RCTNavigatorManager.requestSchedulingJavaScriptNavigation( - this.getNodeHandle(), + (this: any).getNodeHandle(), logError, cb ); @@ -72,6 +73,29 @@ var NavigatorTransitionerIOS = React.createClass({ }, }); +type Route = { + component: Function; + title: string; + passProps: Object; + backButtonTitle: string; + rightButtonTitle: string; + onRightButtonPress: Function; + wrapperStyle: any; +}; + +type State = { + idStack: Array; + routeStack: Array; + requestedTopOfStack: number; + observedTopOfStack: number; + progress: number; + fromIndex: number; + toIndex: number; + makingNavigatorRequest: boolean; + updatingAllIndicesAtOrBeyond: number; +} + +type Event = Object; /** * Think of `` as simply a component that renders an @@ -224,6 +248,8 @@ var NavigatorIOS = React.createClass({ }, + navigator: (undefined: ?Object), + componentWillMount: function() { // Precompute a pack of callbacks that's frequently generated and passed to // instances. @@ -240,7 +266,7 @@ var NavigatorIOS = React.createClass({ }; }, - getInitialState: function() { + getInitialState: function(): State { return { idStack: [getuid()], routeStack: [this.props.initialRoute], @@ -266,7 +292,9 @@ var NavigatorIOS = React.createClass({ }; }, - _handleFocusRequest: function(item) { + _toFocusOnNavigationComplete: (undefined: any), + + _handleFocusRequest: function(item: any) { if (this.state.makingNavigatorRequest) { this._toFocusOnNavigationComplete = item; } else { @@ -274,14 +302,22 @@ var NavigatorIOS = React.createClass({ } }, - _getFocusEmitter: function() { - if (!this._focusEmitter) { - this._focusEmitter = new EventEmitter(); + _focusEmitter: (undefined: ?EventEmitter), + + _getFocusEmitter: function(): EventEmitter { + // Flow not yet tracking assignments to instance fields. + var focusEmitter = this._focusEmitter; + if (!focusEmitter) { + focusEmitter = new EventEmitter(); + this._focusEmitter = focusEmitter; } - return this._focusEmitter; + return focusEmitter; }, - getChildContext: function() { + getChildContext: function(): { + onFocusRequested: Function; + focusEmitter: EventEmitter; + } { return { onFocusRequested: this._handleFocusRequest, focusEmitter: this._getFocusEmitter(), @@ -293,13 +329,13 @@ var NavigatorIOS = React.createClass({ focusEmitter: React.PropTypes.instanceOf(EventEmitter), }, - _tryLockNavigator: function(cb) { + _tryLockNavigator: function(cb: () => void) { this.refs[TRANSITIONER_REF].requestSchedulingNavigation( (acquiredLock) => acquiredLock && cb() ); }, - _handleNavigatorStackChanged: function(e) { + _handleNavigatorStackChanged: function(e: Event) { var newObservedTopOfStack = e.nativeEvent.stackLength - 1; invariant( newObservedTopOfStack <= this.state.requestedTopOfStack, @@ -352,7 +388,7 @@ var NavigatorIOS = React.createClass({ }); }, - push: function(route) { + push: function(route: Route) { invariant(!!route, 'Must supply route to push'); // Make sure all previous requests are caught up first. Otherwise reject. if (this.state.requestedTopOfStack === this.state.observedTopOfStack) { @@ -372,7 +408,7 @@ var NavigatorIOS = React.createClass({ } }, - popN: function(n) { + popN: function(n: number) { if (n === 0) { return; } @@ -406,7 +442,7 @@ var NavigatorIOS = React.createClass({ * `index` specifies the route in the stack that should be replaced. * If it's negative, it counts from the back. */ - replaceAtIndex: function(route, index) { + replaceAtIndex: function(route: Route, index: number) { invariant(!!route, 'Must supply route to replace'); if (index < 0) { index += this.state.routeStack.length; @@ -434,14 +470,14 @@ var NavigatorIOS = React.createClass({ /** * Replaces the top of the navigation stack. */ - replace: function(route) { + replace: function(route: Route) { this.replaceAtIndex(route, -1); }, /** * Replace the current route's parent. */ - replacePrevious: function(route) { + replacePrevious: function(route: Route) { this.replaceAtIndex(route, -2); }, @@ -449,7 +485,7 @@ var NavigatorIOS = React.createClass({ this.popToRoute(this.state.routeStack[0]); }, - popToRoute: function(route) { + popToRoute: function(route: Route) { var indexOfRoute = this.state.routeStack.indexOf(route); invariant( indexOfRoute !== -1, @@ -459,7 +495,7 @@ var NavigatorIOS = React.createClass({ this.popN(numToPop); }, - replacePreviousAndPop: function(route) { + replacePreviousAndPop: function(route: Route) { // Make sure all previous requests are caught up first. Otherwise reject. if (this.state.requestedTopOfStack !== this.state.observedTopOfStack) { return; @@ -476,7 +512,7 @@ var NavigatorIOS = React.createClass({ }); }, - resetTo: function(route) { + resetTo: function(route: Route) { invariant(!!route, 'Must supply route to push'); // Make sure all previous requests are caught up first. Otherwise reject. if (this.state.requestedTopOfStack !== this.state.observedTopOfStack) { @@ -486,7 +522,7 @@ var NavigatorIOS = React.createClass({ this.popToRoute(route); }, - handleNavigationComplete: function(e) { + handleNavigationComplete: function(e: Event) { if (this._toFocusOnNavigationComplete) { this._getFocusEmitter().emit('focus', this._toFocusOnNavigationComplete); this._toFocusOnNavigationComplete = null; @@ -494,7 +530,7 @@ var NavigatorIOS = React.createClass({ this._handleNavigatorStackChanged(e); }, - _routeToStackItem: function(route, i) { + _routeToStackItem: function(route: Route, i: number) { var Component = route.component; var shouldUpdateChild = this.state.updatingAllIndicesAtOrBeyond !== null && this.state.updatingAllIndicesAtOrBeyond >= i; diff --git a/Libraries/Components/ScrollResponder.js b/Libraries/Components/ScrollResponder.js index 471487cd10..c9756908a5 100644 --- a/Libraries/Components/ScrollResponder.js +++ b/Libraries/Components/ScrollResponder.js @@ -7,6 +7,7 @@ * of patent rights can be found in the PATENTS file in the same directory. * * @providesModule ScrollResponder + * @flow */ 'use strict'; @@ -102,10 +103,19 @@ var warning = require('warning'); var IS_ANIMATING_TOUCH_START_THRESHOLD_MS = 16; +type State = { + isTouching: boolean; + lastMomentumScrollBeginTime: number; + lastMomentumScrollEndTime: number; + observedScrollSinceBecomingResponder: boolean; + becameResponderWhileAnimating: boolean; +}; +type Event = Object; + var ScrollResponderMixin = { mixins: [Subscribable.Mixin], statics: RCTScrollViewConsts, - scrollResponderMixinGetInitialState: function() { + scrollResponderMixinGetInitialState: function(): State { return { isTouching: false, lastMomentumScrollBeginTime: 0, @@ -124,7 +134,7 @@ var ScrollResponderMixin = { /** * Invoke this from an `onScroll` event. */ - scrollResponderHandleScrollShouldSetResponder: function() { + scrollResponderHandleScrollShouldSetResponder: function(): boolean { return this.state.isTouching; }, @@ -153,7 +163,7 @@ var ScrollResponderMixin = { * true. * */ - scrollResponderHandleStartShouldSetResponder: function() { + scrollResponderHandleStartShouldSetResponder: function(): boolean { return false; }, @@ -168,7 +178,7 @@ var ScrollResponderMixin = { * * Invoke this from an `onStartShouldSetResponderCapture` event. */ - scrollResponderHandleStartShouldSetResponderCapture: function(e) { + scrollResponderHandleStartShouldSetResponderCapture: function(e: Event): boolean { // First see if we want to eat taps while the keyboard is up var currentlyFocusedTextInput = TextInputState.currentlyFocusedField(); if (!this.props.keyboardShouldPersistTaps && @@ -208,7 +218,7 @@ var ScrollResponderMixin = { * navigation of a swipe gesture higher in the view hierarchy, should be * rejected. */ - scrollResponderHandleTerminationRequest: function() { + scrollResponderHandleTerminationRequest: function(): boolean { return !this.state.observedScrollSinceBecomingResponder; }, @@ -217,7 +227,7 @@ var ScrollResponderMixin = { * * @param {SyntheticEvent} e Event. */ - scrollResponderHandleTouchEnd: function(e) { + scrollResponderHandleTouchEnd: function(e: Event) { var nativeEvent = e.nativeEvent; this.state.isTouching = nativeEvent.touches.length !== 0; this.props.onTouchEnd && this.props.onTouchEnd(e); @@ -226,7 +236,7 @@ var ScrollResponderMixin = { /** * Invoke this from an `onResponderRelease` event. */ - scrollResponderHandleResponderRelease: function(e) { + scrollResponderHandleResponderRelease: function(e: Event) { this.props.onResponderRelease && this.props.onResponderRelease(e); // By default scroll views will unfocus a textField @@ -243,7 +253,7 @@ var ScrollResponderMixin = { } }, - scrollResponderHandleScroll: function(e) { + scrollResponderHandleScroll: function(e: Event) { this.state.observedScrollSinceBecomingResponder = true; this.props.onScroll && this.props.onScroll(e); }, @@ -251,7 +261,7 @@ var ScrollResponderMixin = { /** * Invoke this from an `onResponderGrant` event. */ - scrollResponderHandleResponderGrant: function(e) { + scrollResponderHandleResponderGrant: function(e: Event) { this.state.observedScrollSinceBecomingResponder = false; this.props.onResponderGrant && this.props.onResponderGrant(e); this.state.becameResponderWhileAnimating = this.scrollResponderIsAnimating(); @@ -264,21 +274,21 @@ var ScrollResponderMixin = { * * Invoke this from an `onScrollBeginDrag` event. */ - scrollResponderHandleScrollBeginDrag: function(e) { + scrollResponderHandleScrollBeginDrag: function(e: Event) { this.props.onScrollBeginDrag && this.props.onScrollBeginDrag(e); }, /** * Invoke this from an `onScrollEndDrag` event. */ - scrollResponderHandleScrollEndDrag: function(e) { + scrollResponderHandleScrollEndDrag: function(e: Event) { this.props.onScrollEndDrag && this.props.onScrollEndDrag(e); }, /** * Invoke this from an `onMomentumScrollBegin` event. */ - scrollResponderHandleMomentumScrollBegin: function(e) { + scrollResponderHandleMomentumScrollBegin: function(e: Event) { this.state.lastMomentumScrollBeginTime = Date.now(); this.props.onMomentumScrollBegin && this.props.onMomentumScrollBegin(e); }, @@ -286,7 +296,7 @@ var ScrollResponderMixin = { /** * Invoke this from an `onMomentumScrollEnd` event. */ - scrollResponderHandleMomentumScrollEnd: function(e) { + scrollResponderHandleMomentumScrollEnd: function(e: Event) { this.state.lastMomentumScrollEndTime = Date.now(); this.props.onMomentumScrollEnd && this.props.onMomentumScrollEnd(e); }, @@ -302,7 +312,7 @@ var ScrollResponderMixin = { * * @param {SyntheticEvent} e Touch Start event. */ - scrollResponderHandleTouchStart: function(e) { + scrollResponderHandleTouchStart: function(e: Event) { this.state.isTouching = true; this.props.onTouchStart && this.props.onTouchStart(e); }, @@ -318,7 +328,7 @@ var ScrollResponderMixin = { * * @param {SyntheticEvent} e Touch Start event. */ - scrollResponderHandleTouchMove: function(e) { + scrollResponderHandleTouchMove: function(e: Event) { this.props.onTouchMove && this.props.onTouchMove(e); }, @@ -327,7 +337,7 @@ var ScrollResponderMixin = { * view is currently animating. This is particularly useful to know when * a touch has just started or ended. */ - scrollResponderIsAnimating: function() { + scrollResponderIsAnimating: function(): boolean { var now = Date.now(); var timeSinceLastMomentumScrollEnd = now - this.state.lastMomentumScrollEndTime; var isAnimating = timeSinceLastMomentumScrollEnd < IS_ANIMATING_TOUCH_START_THRESHOLD_MS || @@ -340,7 +350,7 @@ var ScrollResponderMixin = { * This is currently used to help focus on child textview's, but this * can also be used to quickly scroll to any element we want to focus */ - scrollResponderScrollTo: function(offsetX, offsetY) { + scrollResponderScrollTo: function(offsetX: number, offsetY: number) { RCTUIManagerDeprecated.scrollTo(this.getNodeHandle(), offsetX, offsetY); }, @@ -348,7 +358,7 @@ var ScrollResponderMixin = { * A helper function to zoom to a specific rect in the scrollview. * @param {object} rect Should have shape {x, y, w, h} */ - scrollResponderZoomTo: function(rect) { + scrollResponderZoomTo: function(rect: { x: number; y: number; w: number; h: number; }) { RCTUIManagerDeprecated.zoomToRect(this.getNodeHandle(), rect); }, @@ -357,7 +367,7 @@ var ScrollResponderMixin = { * parent view. Note that any module using this mixin needs to return * the parent view's ref in getScrollViewRef() in order to use this method */ - scrollResponderScrollNativeHandleToKeyboard: function(nodeHandle, additionalOffset) { + scrollResponderScrollNativeHandleToKeyboard: function(nodeHandle: any, additionalOffset?: number) { this.additionalScrollOffset = additionalOffset || 0; RCTUIManager.measureLayout( nodeHandle, @@ -377,7 +387,7 @@ var ScrollResponderMixin = { * @param {number} width Width of the text input. * @param {number} height Height of the text input. */ - scrollResponderInputMeasureAndScrollToKeyboard: function(left, top, width, height) { + scrollResponderInputMeasureAndScrollToKeyboard: function(left: number, top: number, width: number, height: number) { if (this.keyboardWillOpenTo) { var scrollOffsetY = top - this.keyboardWillOpenTo.endCoordinates.screenY + height + @@ -387,7 +397,7 @@ var ScrollResponderMixin = { this.additionalOffset = 0; }, - scrollResponderTextInputFocusError: function(e) { + scrollResponderTextInputFocusError: function(e: Event) { console.error('Error measuring text field: ', e); }, @@ -434,12 +444,12 @@ var ScrollResponderMixin = { * relevant to you. (For example, only if you receive these callbacks after * you had explicitly focused a node etc). */ - scrollResponderKeyboardWillShow: function(e) { + scrollResponderKeyboardWillShow: function(e: Event) { this.keyboardWillOpenTo = e; this.props.onKeyboardWillShow && this.props.onKeyboardWillShow(e); }, - scrollResponderKeyboardWillHide: function(e) { + scrollResponderKeyboardWillHide: function(e: Event) { this.keyboardWillOpenTo = null; this.props.onKeyboardWillHide && this.props.onKeyboardWillHide(e); }, diff --git a/Libraries/Components/ScrollView/ScrollView.js b/Libraries/Components/ScrollView/ScrollView.js index 5f4e8700d1..92cdab05ac 100644 --- a/Libraries/Components/ScrollView/ScrollView.js +++ b/Libraries/Components/ScrollView/ScrollView.js @@ -7,6 +7,7 @@ * of patent rights can be found in the PATENTS file in the same directory. * * @providesModule ScrollView + * @flow */ 'use strict'; @@ -178,15 +179,15 @@ var ScrollView = React.createClass({ return this.scrollResponderMixinGetInitialState(); }, - setNativeProps: function(props) { + setNativeProps: function(props: Object) { this.refs[SCROLLVIEW].setNativeProps(props); }, - getInnerViewNode: function() { + getInnerViewNode: function(): any { return this.refs[INNERVIEW].getNodeHandle(); }, - scrollTo: function(destY, destX) { + scrollTo: function(destY?: number, destX?: number) { RCTUIManager.scrollTo( this.getNodeHandle(), destX || 0, @@ -202,7 +203,7 @@ var ScrollView = React.createClass({ if (__DEV__ && this.props.style) { var style = flattenStyle(this.props.style); var childLayoutProps = ['alignItems', 'justifyContent'] - .filter((prop) => style[prop] !== undefined); + .filter((prop) => style && style[prop] !== undefined); invariant( childLayoutProps.length === 0, 'ScrollView child layout (' + JSON.stringify(childLayoutProps) + @@ -250,7 +251,7 @@ var ScrollView = React.createClass({ keyboardDismissMode: this.props.keyboardDismissMode ? keyboardDismissModeConstants[this.props.keyboardDismissMode] : undefined, - style: [styles.base, this.props.style], + style: ([styles.base, this.props.style]: ?Array), onTouchStart: this.scrollResponderHandleTouchStart, onTouchMove: this.scrollResponderHandleTouchMove, onTouchEnd: this.scrollResponderHandleTouchEnd, @@ -279,6 +280,10 @@ var ScrollView = React.createClass({ ScrollViewClass = AndroidScrollView; } } + invariant( + ScrollViewClass !== undefined, + 'ScrollViewClass must not be undefined' + ); return ( diff --git a/Libraries/Components/SliderIOS/SliderIOS.js b/Libraries/Components/SliderIOS/SliderIOS.js index 97c848dcce..aac9798d3c 100644 --- a/Libraries/Components/SliderIOS/SliderIOS.js +++ b/Libraries/Components/SliderIOS/SliderIOS.js @@ -7,6 +7,7 @@ * of patent rights can be found in the PATENTS file in the same directory. * * @providesModule SliderIOS + * @flow */ 'use strict'; @@ -21,6 +22,8 @@ var createReactIOSNativeComponentClass = require('createReactIOSNativeComponentClass'); var merge = require('merge'); +type Event = Object; + var SliderIOS = React.createClass({ mixins: [NativeMethodsMixin], @@ -52,7 +55,7 @@ var SliderIOS = React.createClass({ onSlidingComplete: PropTypes.func, }, - _onValueChange: function(event) { + _onValueChange: function(event: Event) { this.props.onChange && this.props.onChange(event); if (event.nativeEvent.continuous) { this.props.onValueChange && diff --git a/Libraries/Components/StaticRenderer.js b/Libraries/Components/StaticRenderer.js index 0986fc64f7..8800198a5d 100644 --- a/Libraries/Components/StaticRenderer.js +++ b/Libraries/Components/StaticRenderer.js @@ -7,6 +7,7 @@ * of patent rights can be found in the PATENTS file in the same directory. * * @provides StaticRenderer + * @flow */ 'use strict'; @@ -18,11 +19,11 @@ var StaticRenderer = React.createClass({ render: React.PropTypes.func.isRequired, }, - shouldComponentUpdate: function(nextProps) { + shouldComponentUpdate: function(nextProps: { shouldUpdate: boolean }): boolean { return nextProps.shouldUpdate; }, - render: function() { + render: function(): ReactElement { return this.props.render(); }, }); diff --git a/Libraries/Components/Subscribable.js b/Libraries/Components/Subscribable.js index 797aabf297..06b7e0ce4b 100644 --- a/Libraries/Components/Subscribable.js +++ b/Libraries/Components/Subscribable.js @@ -7,6 +7,7 @@ * of patent rights can be found in the PATENTS file in the same directory. * * @providesModule Subscribable + * @flow */ 'use strict'; @@ -73,8 +74,13 @@ var logError = require('logError'); var SUBSCRIBABLE_INTERNAL_EVENT = 'subscriptionEvent'; +type Data = Object; +type EventMapping = (_: Data) => Data; class Subscribable { + _eventMapping: EventMapping; + _lastData: Data; + /** * Creates a new Subscribable object * @@ -88,7 +94,7 @@ class Subscribable { * The resolved data will be transformed with the eventMapping before it * gets emitted. */ - constructor(eventEmitter, eventName, eventMapping, getInitData) { + constructor(eventEmitter: EventEmitter, eventName: string, eventMapping?: EventMapping, getInitData?: Function) { this._internalEmitter = new EventEmitter(); this._eventMapping = eventMapping || (data => data); @@ -106,7 +112,7 @@ class Subscribable { /** * Returns the last data emitted from the Subscribable, or undefined */ - get() { + get(): Data { return this._lastData; } @@ -138,7 +144,7 @@ class Subscribable { * } * Call `remove` to terminate the subscription before unmounting */ - subscribe(lifespan, callback, context) { + subscribe(lifespan: { addUnmountCallback: Function }, callback: Function, context: Object) { invariant( typeof lifespan.addUnmountCallback === 'function', 'Must provide a valid lifespan, which provides a way to add a ' + @@ -169,7 +175,7 @@ class Subscribable { * Callback for the initial data resolution. Currently behaves the same as * `_handleEmit`, but we may eventually want to keep track of the difference */ - _handleInitData(dataInput) { + _handleInitData(dataInput: Data) { var emitData = this._eventMapping(dataInput); this._lastData = emitData; this._internalEmitter.emit(SUBSCRIBABLE_INTERNAL_EVENT, emitData); @@ -179,7 +185,7 @@ class Subscribable { * Handle new data emissions. Pass the data through our eventMapping * transformation, store it for later `get()`ing, and emit it for subscribers */ - _handleEmit(dataInput) { + _handleEmit(dataInput: Data) { var emitData = this._eventMapping(dataInput); this._lastData = emitData; this._internalEmitter.emit(SUBSCRIBABLE_INTERNAL_EVENT, emitData); @@ -280,12 +286,13 @@ Subscribable.Mixin = { if (!this._localSubscribables) { return; } - var emitterSubscribables; Object.keys(this._localSubscribables).forEach((eventEmitter) => { - emitterSubscribables = this._localSubscribables[eventEmitter]; - Object.keys(emitterSubscribables).forEach((eventName) => { - emitterSubscribables[eventName].cleanup(); - }); + var emitterSubscribables = this._localSubscribables[eventEmitter]; + if (emitterSubscribables) { + Object.keys(emitterSubscribables).forEach((eventName) => { + emitterSubscribables[eventName].cleanup(); + }); + } }); this._localSubscribables = null; }, diff --git a/Libraries/Components/SwitchIOS/SwitchIOS.ios.js b/Libraries/Components/SwitchIOS/SwitchIOS.ios.js index 417b29d11f..7022279475 100644 --- a/Libraries/Components/SwitchIOS/SwitchIOS.ios.js +++ b/Libraries/Components/SwitchIOS/SwitchIOS.ios.js @@ -7,6 +7,7 @@ * of patent rights can be found in the PATENTS file in the same directory. * * @providesModule SwitchIOS + * @flow * * This is a controlled component version of RCTSwitch. */ @@ -23,6 +24,13 @@ var merge = require('merge'); var SWITCH = 'switch'; +type DefaultProps = { + value: boolean; + disabled: boolean; +}; + +type Event = Object; + /** * Use `SwitchIOS` to render a boolean input on iOS. This is * a controlled component, so you must hook in to the `onValueChange` callback @@ -67,14 +75,14 @@ var SwitchIOS = React.createClass({ tintColor: PropTypes.string, }, - getDefaultProps: function() { + getDefaultProps: function(): DefaultProps { return { value: false, disabled: false, }; }, - _onChange: function(event) { + _onChange: function(event: Event) { this.props.onChange && this.props.onChange(event); this.props.onValueChange && this.props.onValueChange(event.nativeEvent.value); diff --git a/Libraries/Components/TabBarIOS/TabBarIOS.ios.js b/Libraries/Components/TabBarIOS/TabBarIOS.ios.js index 3e09a6c9b7..fa82dd4cbc 100644 --- a/Libraries/Components/TabBarIOS/TabBarIOS.ios.js +++ b/Libraries/Components/TabBarIOS/TabBarIOS.ios.js @@ -7,6 +7,7 @@ * of patent rights can be found in the PATENTS file in the same directory. * * @providesModule TabBarIOS + * @flow */ 'use strict'; diff --git a/Libraries/Components/TabBarIOS/TabBarItemIOS.ios.js b/Libraries/Components/TabBarIOS/TabBarItemIOS.ios.js index 744c0a1f7d..11b02ab396 100644 --- a/Libraries/Components/TabBarIOS/TabBarItemIOS.ios.js +++ b/Libraries/Components/TabBarIOS/TabBarItemIOS.ios.js @@ -7,6 +7,7 @@ * of patent rights can be found in the PATENTS file in the same directory. * * @providesModule TabBarItemIOS + * @flow */ 'use strict'; @@ -43,7 +44,7 @@ var TabBarItemIOS = React.createClass({ } }, - componentWillReceiveProps: function(nextProps) { + componentWillReceiveProps: function(nextProps: { selected: boolean }) { if (this.state.hasBeenSelected || nextProps.selected) { this.setState({hasBeenSelected: true}); } diff --git a/Libraries/Components/TextInput/TextInput.ios.js b/Libraries/Components/TextInput/TextInput.ios.js index 6affc40422..2653421be3 100644 --- a/Libraries/Components/TextInput/TextInput.ios.js +++ b/Libraries/Components/TextInput/TextInput.ios.js @@ -7,6 +7,7 @@ * of patent rights can be found in the PATENTS file in the same directory. * * @providesModule TextInput + * @flow */ 'use strict'; @@ -63,6 +64,12 @@ var notMultiline = { onSubmitEditing: true, }; +type DefaultProps = { + bufferDelay: number; +}; + +type Event = Object; + /** * A foundational component for inputting text into the app via a * keyboard. Props provide configurability for several features, such as auto- @@ -206,12 +213,12 @@ var TextInput = React.createClass({ validAttributes: RCTTextFieldAttributes, }, - isFocused: function() { + isFocused: function(): boolean { return TextInputState.currentlyFocusedField() === this.refs.input.getNativeNode(); }, - getDefaultProps: function() { + getDefaultProps: function(): DefaultProps { return { bufferDelay: 100, }; @@ -229,6 +236,8 @@ var TextInput = React.createClass({ focusEmitter: React.PropTypes.instanceOf(EventEmitter), }, + _focusSubscription: (undefined: ?Function), + componentDidMount: function() { if (!this.context.focusEmitter) { if (this.props.autoFocus) { @@ -255,7 +264,9 @@ var TextInput = React.createClass({ this._focusSubscription && this._focusSubscription.remove(); }, - componentWillReceiveProps: function(newProps) { + _bufferTimeout: (undefined: ?number), + + componentWillReceiveProps: function(newProps: {value: any}) { if (newProps.value !== this.props.value) { if (!this.isFocused()) { // Set the value immediately if the input is not focused since that @@ -385,17 +396,17 @@ var TextInput = React.createClass({ ); }, - _onFocus: function(event) { + _onFocus: function(event: Event) { if (this.props.onFocus) { this.props.onFocus(event); } }, - _onPress: function(event) { + _onPress: function(event: Event) { this.focus(); }, - _onChange: function(event) { + _onChange: function(event: Event) { if (this.props.controlled && event.nativeEvent.text !== this.props.value) { this.refs.input.setNativeProps({text: this.props.value}); } @@ -403,14 +414,14 @@ var TextInput = React.createClass({ this.props.onChangeText && this.props.onChangeText(event.nativeEvent.text); }, - _onBlur: function(event) { + _onBlur: function(event: Event) { this.blur(); if (this.props.onBlur) { this.props.onBlur(event); } }, - _onSelectionChange: function(event) { + _onSelectionChange: function(event: Event) { if (this.props.selectionState) { var selection = event.nativeEvent.selection; this.props.selectionState.update(selection.start, selection.end); @@ -418,7 +429,7 @@ var TextInput = React.createClass({ this.props.onSelectionChange && this.props.onSelectionChange(event); }, - _onTextInput: function(event) { + _onTextInput: function(event: Event) { this.props.onTextInput && this.props.onTextInput(event); var counter = event.nativeEvent.eventCounter; if (counter > this.state.mostRecentEventCounter) { diff --git a/Libraries/Components/TextInput/TextInputState.js b/Libraries/Components/TextInput/TextInputState.js index 5cd66c1f0c..7c5af440e7 100644 --- a/Libraries/Components/TextInput/TextInputState.js +++ b/Libraries/Components/TextInput/TextInputState.js @@ -7,6 +7,7 @@ * of patent rights can be found in the PATENTS file in the same directory. * * @providesModule TextInputState + * @flow * * This class is responsible for coordinating the "focused" * state for TextInputs. All calls relating to the keyboard @@ -20,13 +21,13 @@ var TextInputState = { /** * Internal state */ - _currentlyFocusedID: null, + _currentlyFocusedID: (null: ?string), /** * Returns the ID of the currently focused text field, if one exists * If no text field is focused it returns null */ - currentlyFocusedField: function() { + currentlyFocusedField: function(): ?string { return this._currentlyFocusedID; }, @@ -35,7 +36,7 @@ var TextInputState = { * Focuses the specified text field * noop if the text field was already focused */ - focusTextInput: function(textFieldID) { + focusTextInput: function(textFieldID: string) { if (this._currentlyFocusedID != textFieldID && textFieldID != null) { this._currentlyFocusedID = textFieldID; RCTUIManager.focus(textFieldID); @@ -47,7 +48,7 @@ var TextInputState = { * Unfocuses the specified text field * noop if it wasn't focused */ - blurTextInput: function(textFieldID) { + blurTextInput: function(textFieldID: string) { if (this._currentlyFocusedID == textFieldID && textFieldID != null) { this._currentlyFocusedID = null; RCTUIManager.blur(textFieldID); diff --git a/Libraries/Components/Touchable/TouchableBounce.js b/Libraries/Components/Touchable/TouchableBounce.js index c7e6af5d1d..d575657e9e 100644 --- a/Libraries/Components/Touchable/TouchableBounce.js +++ b/Libraries/Components/Touchable/TouchableBounce.js @@ -7,6 +7,7 @@ * of patent rights can be found in the PATENTS file in the same directory. * * @providesModule TouchableBounce + * @flow */ 'use strict'; @@ -20,6 +21,10 @@ var merge = require('merge'); var copyProperties = require('copyProperties'); var onlyChild = require('onlyChild'); +type State = { + animationID: ?number; +}; + /** * When the scroll view is disabled, this defines how far your touch may move * off of the button, before deactivating the button. Once deactivated, try @@ -47,11 +52,17 @@ var TouchableBounce = React.createClass({ onPressAnimationComplete: React.PropTypes.func, }, - getInitialState: function() { + getInitialState: function(): State { return merge(this.touchableGetInitialState(), {animationID: null}); }, - bounceTo: function(value, velocity, bounciness, fromValue, callback) { + bounceTo: function( + value: number, + velocity: number, + bounciness: number, + fromValue?: ?Function | number, + callback?: ?Function + ) { if (POPAnimation) { this.state.animationID && this.removeAnimation(this.state.animationID); var anim = { @@ -60,6 +71,7 @@ var TouchableBounce = React.createClass({ toValue: [value, value], velocity: [velocity, velocity], springBounciness: bounciness, + fromValue: (undefined: ?any), }; if (fromValue) { anim.fromValue = [fromValue, fromValue]; @@ -90,8 +102,9 @@ var TouchableBounce = React.createClass({ }, touchableHandlePress: function() { - if (this.props.onPressWithCompletion) { - this.props.onPressWithCompletion( + var onPressWithCompletion = this.props.onPressWithCompletion; + if (onPressWithCompletion) { + onPressWithCompletion( this.bounceTo.bind(this, 1, 10, 10, 0.93, this.props.onPressAnimationComplete) ); return; @@ -101,11 +114,11 @@ var TouchableBounce = React.createClass({ this.props.onPress && this.props.onPress(); }, - touchableGetPressRectOffset: function() { + touchableGetPressRectOffset: function(): typeof PRESS_RECT_OFFSET { return PRESS_RECT_OFFSET; // Always make sure to predeclare a constant! }, - touchableGetHighlightDelayMS: function() { + touchableGetHighlightDelayMS: function(): number { return 0; }, diff --git a/Libraries/Components/Touchable/TouchableHighlight.js b/Libraries/Components/Touchable/TouchableHighlight.js index 6feaef0997..d2089fb563 100644 --- a/Libraries/Components/Touchable/TouchableHighlight.js +++ b/Libraries/Components/Touchable/TouchableHighlight.js @@ -10,6 +10,8 @@ */ 'use strict'; +// Note (avik): add @flow when Flow supports spread properties in propTypes + var NativeMethodsMixin = require('NativeMethodsMixin'); var React = require('React'); var ReactIOSViewAttributes = require('ReactIOSViewAttributes'); diff --git a/Libraries/Components/Touchable/TouchableOpacity.js b/Libraries/Components/Touchable/TouchableOpacity.js index 065f1861a9..fd9d059714 100644 --- a/Libraries/Components/Touchable/TouchableOpacity.js +++ b/Libraries/Components/Touchable/TouchableOpacity.js @@ -10,6 +10,8 @@ */ 'use strict'; +// Note (avik): add @flow when Flow supports spread properties in propTypes + var NativeMethodsMixin = require('NativeMethodsMixin'); var POPAnimationMixin = require('POPAnimationMixin'); var React = require('React'); diff --git a/Libraries/Components/Touchable/TouchableWithoutFeedback.js b/Libraries/Components/Touchable/TouchableWithoutFeedback.js index 53ac49d05e..1b1178ebbe 100644 --- a/Libraries/Components/Touchable/TouchableWithoutFeedback.js +++ b/Libraries/Components/Touchable/TouchableWithoutFeedback.js @@ -7,6 +7,7 @@ * of patent rights can be found in the PATENTS file in the same directory. * * @providesModule TouchableWithoutFeedback + * @flow */ 'use strict'; @@ -23,6 +24,7 @@ var onlyChild = require('onlyChild'); */ var PRESS_RECT_OFFSET = {top: 20, left: 20, right: 20, bottom: 30}; +type Event = Object; /** * Do not use unless you have a very good reason. All the elements that @@ -51,7 +53,7 @@ var TouchableWithoutFeedback = React.createClass({ * `Touchable.Mixin` self callbacks. The mixin will invoke these if they are * defined on your component. */ - touchableHandlePress: function(e) { + touchableHandlePress: function(e: Event) { this.props.onPress && this.props.onPress(e); }, @@ -67,18 +69,19 @@ var TouchableWithoutFeedback = React.createClass({ this.props.onLongPress && this.props.onLongPress(); }, - touchableGetPressRectOffset: function() { + touchableGetPressRectOffset: function(): typeof PRESS_RECT_OFFSET { return PRESS_RECT_OFFSET; // Always make sure to predeclare a constant! }, - touchableGetHighlightDelayMS: function() { + touchableGetHighlightDelayMS: function(): number { return 0; }, - render: function() { + render: function(): ReactElement { // Note(vjeux): use cloneWithProps once React has been upgraded var child = onlyChild(this.props.children); - return React.cloneElement(child, { + // Note(avik): remove dynamic typecast once Flow has been upgraded + return (React: any).cloneElement(child, { accessible: true, testID: this.props.testID, onStartShouldSetResponder: this.touchableHandleStartShouldSetResponder, diff --git a/Libraries/Components/Touchable/ensureComponentIsNative.js b/Libraries/Components/Touchable/ensureComponentIsNative.js index 5a15f424f5..944093f78e 100644 --- a/Libraries/Components/Touchable/ensureComponentIsNative.js +++ b/Libraries/Components/Touchable/ensureComponentIsNative.js @@ -7,12 +7,13 @@ * of patent rights can be found in the PATENTS file in the same directory. * * @providesModule ensureComponentIsNative + * @flow */ 'use strict'; var invariant = require('invariant'); -var ensureComponentIsNative = function(component) { +var ensureComponentIsNative = function(component: any) { invariant( component && typeof component.setNativeProps === 'function', 'Touchable child must either be native or forward setNativeProps to a ' + diff --git a/Libraries/Components/View/View.js b/Libraries/Components/View/View.js index 67a4ac9414..882adf4208 100644 --- a/Libraries/Components/View/View.js +++ b/Libraries/Components/View/View.js @@ -7,6 +7,7 @@ * of patent rights can be found in the PATENTS file in the same directory. * * @providesModule View + * @flow */ 'use strict'; diff --git a/Libraries/Components/View/ViewStylePropTypes.js b/Libraries/Components/View/ViewStylePropTypes.js index e936f035a1..bb22c6b265 100644 --- a/Libraries/Components/View/ViewStylePropTypes.js +++ b/Libraries/Components/View/ViewStylePropTypes.js @@ -7,6 +7,7 @@ * of patent rights can be found in the PATENTS file in the same directory. * * @providesModule ViewStylePropTypes + * @flow */ 'use strict'; diff --git a/Libraries/Components/WebView/WebView.ios.js b/Libraries/Components/WebView/WebView.ios.js index 8768ab4041..867143b8c5 100644 --- a/Libraries/Components/WebView/WebView.ios.js +++ b/Libraries/Components/WebView/WebView.ios.js @@ -7,6 +7,7 @@ * of patent rights can be found in the PATENTS file in the same directory. * * @providesModule WebView + * @flow */ 'use strict'; @@ -24,6 +25,8 @@ var merge = require('merge'); var PropTypes = React.PropTypes; var RCTWebViewManager = require('NativeModules').WebViewManager; +var invariant = require('invariant'); + var RCT_WEBVIEW_REF = 'webview'; var WebViewState = keyMirror({ @@ -41,6 +44,14 @@ var NavigationType = { other: RCTWebViewManager.NavigationType.Other, }; +type ErrorEvent = { + domain: any; + code: any; + description: any; +} + +type Event = Object; + var WebView = React.createClass({ statics: { NavigationType: NavigationType, @@ -61,7 +72,7 @@ var WebView = React.createClass({ getInitialState: function() { return { viewState: WebViewState.IDLE, - lastErrorEvent: null, + lastErrorEvent: (null: ?ErrorEvent), startInLoadingState: true, }; }, @@ -79,6 +90,10 @@ var WebView = React.createClass({ otherView = this.props.renderLoading(); } else if (this.state.viewState === WebViewState.ERROR) { var errorEvent = this.state.lastErrorEvent; + invariant( + errorEvent != null, + 'lastErrorEvent expected to be non-null' + ); otherView = this.props.renderError( errorEvent.domain, errorEvent.code, @@ -132,21 +147,21 @@ var WebView = React.createClass({ * We return an event with a bunch of fields including: * url, title, loading, canGoBack, canGoForward */ - updateNavigationState: function(event) { + updateNavigationState: function(event: Event) { if (this.props.onNavigationStateChange) { this.props.onNavigationStateChange(event.nativeEvent); } }, - getWebWiewHandle: function() { + getWebWiewHandle: function(): any { return this.refs[RCT_WEBVIEW_REF].getNodeHandle(); }, - onLoadingStart: function(event) { + onLoadingStart: function(event: Event) { this.updateNavigationState(event); }, - onLoadingError: function(event) { + onLoadingError: function(event: Event) { event.persist(); // persist this event because we need to store it console.error("encountered an error loading page", event.nativeEvent); @@ -156,7 +171,7 @@ var WebView = React.createClass({ }); }, - onLoadingFinish: function(event) { + onLoadingFinish: function(event: Event) { this.setState({ viewState: WebViewState.IDLE, });