Skip to content

Commit

Permalink
Make Image component flow strict-local (facebook#40729)
Browse files Browse the repository at this point in the history
Summary:
Pull Request resolved: facebook#40729

This improves the type definition of the `Image` modules (the common module interface, the platform-specific implementations and the shared types module). It makes them `flow strict-local` and explicitly defines the type signature of some functions typed as `any` before.

Changelog: [internal]

Reviewed By: sullenor

Differential Revision: D50014569

fbshipit-source-id: 1d1ef1d9b405765524114506189108fb31906a93
  • Loading branch information
rubennorte authored and facebook-github-bot committed Oct 9, 2023
1 parent 05ee8bd commit 5108ca2
Show file tree
Hide file tree
Showing 4 changed files with 70 additions and 67 deletions.
62 changes: 31 additions & 31 deletions packages/react-native/Libraries/Image/Image.android.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,10 +4,11 @@
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*
* @flow
* @flow strict-local
* @format
*/

import type {ImageStyleProp} from '../StyleSheet/StyleSheet';
import type {RootTag} from '../Types/RootTagTypes';
import type {AbstractImageAndroid, ImageAndroid} from './ImageTypes.flow';

Expand Down Expand Up @@ -37,9 +38,9 @@ function generateRequestId() {
function getSize(
url: string,
success: (width: number, height: number) => void,
failure?: (error: any) => void,
): any {
return NativeImageLoaderAndroid.getSize(url)
failure?: (error: mixed) => void,
): void {
NativeImageLoaderAndroid.getSize(url)
.then(function (sizes) {
success(sizes.width, sizes.height);
})
Expand All @@ -61,9 +62,9 @@ function getSizeWithHeaders(
url: string,
headers: {[string]: string, ...},
success: (width: number, height: number) => void,
failure?: (error: any) => void,
): any {
return NativeImageLoaderAndroid.getSizeWithHeaders(url, headers)
failure?: (error: mixed) => void,
): void {
NativeImageLoaderAndroid.getSizeWithHeaders(url, headers)
.then(function (sizes) {
success(sizes.width, sizes.height);
})
Expand All @@ -79,19 +80,22 @@ function prefetchWithMetadata(
url: string,
queryRootName: string,
rootTag?: ?RootTag,
callback: ?Function,
): any {
callback: ?(requestId: number) => void,
): Promise<boolean> {
// TODO: T79192300 Log queryRootName and rootTag
prefetch(url, callback);
return prefetch(url, callback);
}

function prefetch(url: string, callback: ?Function): any {
function prefetch(
url: string,
callback: ?(requestId: number) => void,
): Promise<boolean> {
const requestId = generateRequestId();
callback && callback(requestId);
return NativeImageLoaderAndroid.prefetchImage(url, requestId);
}

function abortPrefetch(requestId: number) {
function abortPrefetch(requestId: number): void {
NativeImageLoaderAndroid.abortRequest(requestId);
}

Expand All @@ -103,7 +107,7 @@ function abortPrefetch(requestId: number) {
async function queryCache(
urls: Array<string>,
): Promise<{[string]: 'memory' | 'disk' | 'disk/memory', ...}> {
return await NativeImageLoaderAndroid.queryCache(urls);
return NativeImageLoaderAndroid.queryCache(urls);
}

/**
Expand Down Expand Up @@ -131,7 +135,7 @@ let BaseImage: AbstractImageAndroid = React.forwardRef(
);
}

if (props.defaultSource && props.loadingIndicatorSource) {
if (props.defaultSource != null && props.loadingIndicatorSource != null) {
throw new Error(
'The <Image> component cannot have defaultSource and loadingIndicatorSource at the same time. Please use either defaultSource or loadingIndicatorSource.',
);
Expand All @@ -140,14 +144,17 @@ let BaseImage: AbstractImageAndroid = React.forwardRef(
let style;
let sources;
if (Array.isArray(source)) {
// $FlowFixMe[underconstrained-implicit-instantiation]
style = flattenStyle([styles.base, props.style]);
style = flattenStyle<ImageStyleProp>([styles.base, props.style]);
sources = source;
} else {
// $FlowFixMe[incompatible-type]
const {width = props.width, height = props.height, uri} = source;
// $FlowFixMe[underconstrained-implicit-instantiation]
style = flattenStyle([{width, height}, styles.base, props.style]);
const {uri} = source;
const width = source.width ?? props.width;
const height = source.height ?? props.height;
style = flattenStyle<ImageStyleProp>([
{width, height},
styles.base,
props.style,
]);
sources = [source];
if (uri === '') {
console.warn('source.uri should not be an empty string');
Expand Down Expand Up @@ -184,16 +191,11 @@ let BaseImage: AbstractImageAndroid = React.forwardRef(
},
};

const objectFit =
// $FlowFixMe[prop-missing]
style && style.objectFit
? // $FlowFixMe[incompatible-call]
convertObjectFitToResizeMode(style.objectFit)
: null;
// $FlowFixMe[prop-missing]
const objectFit = style?.objectFit
? convertObjectFitToResizeMode(style.objectFit)
: null;
const resizeMode =
// $FlowFixMe[prop-missing]
objectFit || props.resizeMode || (style && style.resizeMode) || 'cover';
objectFit || props.resizeMode || style?.resizeMode || 'cover';

return (
<ImageAnalyticsTagContext.Consumer>
Expand All @@ -213,7 +215,6 @@ let BaseImage: AbstractImageAndroid = React.forwardRef(
<TextInlineImageNativeComponent
// $FlowFixMe[incompatible-type]
style={style}
// $FlowFixMe[incompatible-type]
resizeMode={resizeMode}
headers={nativeProps.headers}
src={sources}
Expand All @@ -225,7 +226,6 @@ let BaseImage: AbstractImageAndroid = React.forwardRef(
return (
<ImageViewNativeComponent
{...nativePropsWithAnalytics}
// $FlowFixMe[incompatible-type]
resizeMode={resizeMode}
/>
);
Expand Down
58 changes: 30 additions & 28 deletions packages/react-native/Libraries/Image/Image.ios.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,14 +4,15 @@
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*
* @flow
* @flow strict-local
* @format
*/

import type {ImageStyleProp} from '../StyleSheet/StyleSheet';
import type {ImageStyle, ImageStyleProp} from '../StyleSheet/StyleSheet';
import type {RootTag} from '../Types/RootTagTypes';
import type {AbstractImageIOS, ImageIOS} from './ImageTypes.flow';

import {createRootTag} from '../ReactNative/RootTag';
import flattenStyle from '../StyleSheet/flattenStyle';
import StyleSheet from '../StyleSheet/StyleSheet';
import ImageAnalyticsTagContext from './ImageAnalyticsTagContext';
Expand All @@ -26,8 +27,8 @@ import * as React from 'react';
function getSize(
uri: string,
success: (width: number, height: number) => void,
failure?: (error: any) => void,
) {
failure?: (error: mixed) => void,
): void {
NativeImageLoaderIOS.getSize(uri)
.then(([width, height]) => success(width, height))
.catch(
Expand All @@ -42,9 +43,9 @@ function getSizeWithHeaders(
uri: string,
headers: {[string]: string, ...},
success: (width: number, height: number) => void,
failure?: (error: any) => void,
): any {
return NativeImageLoaderIOS.getSizeWithHeaders(uri, headers)
failure?: (error: mixed) => void,
): void {
NativeImageLoaderIOS.getSizeWithHeaders(uri, headers)
.then(function (sizes) {
success(sizes.width, sizes.height);
})
Expand All @@ -60,29 +61,28 @@ function prefetchWithMetadata(
url: string,
queryRootName: string,
rootTag?: ?RootTag,
): any {
): Promise<boolean> {
if (NativeImageLoaderIOS.prefetchImageWithMetadata) {
// number params like rootTag cannot be nullable before TurboModules is available
return NativeImageLoaderIOS.prefetchImageWithMetadata(
url,
queryRootName,
// NOTE: RootTag type
// $FlowFixMe[incompatible-call] RootTag: number is incompatible with RootTag
rootTag ? rootTag : 0,
rootTag != null ? rootTag : createRootTag(0),
);
} else {
return NativeImageLoaderIOS.prefetchImage(url);
}
}

function prefetch(url: string): any {
function prefetch(url: string): Promise<boolean> {
return NativeImageLoaderIOS.prefetchImage(url);
}

async function queryCache(
urls: Array<string>,
): Promise<{[string]: 'memory' | 'disk' | 'disk/memory', ...}> {
return await NativeImageLoaderIOS.queryCache(urls);
return NativeImageLoaderIOS.queryCache(urls);
}

/**
Expand All @@ -100,16 +100,23 @@ let BaseImage: AbstractImageIOS = React.forwardRef((props, forwardedRef) => {
};

let sources;
let style: ImageStyleProp;
let style: ImageStyle;

if (Array.isArray(source)) {
// $FlowFixMe[underconstrained-implicit-instantiation]
style = flattenStyle([styles.base, props.style]) || {};
style =
flattenStyle<ImageStyleProp>([styles.base, props.style]) ||
({}: ImageStyle);
sources = source;
} else {
// $FlowFixMe[incompatible-type]
const {width = props.width, height = props.height, uri} = source;
// $FlowFixMe[underconstrained-implicit-instantiation]
style = flattenStyle([{width, height}, styles.base, props.style]) || {};
const {uri} = source;
const width = source.width ?? props.width;
const height = source.height ?? props.height;
style =
flattenStyle<ImageStyleProp>([
{width, height},
styles.base,
props.style,
]) || ({}: ImageStyle);
sources = [source];

if (uri === '') {
Expand All @@ -118,16 +125,12 @@ let BaseImage: AbstractImageIOS = React.forwardRef((props, forwardedRef) => {
}

const objectFit =
// $FlowFixMe[prop-missing]
style && style.objectFit
? // $FlowFixMe[incompatible-call]
convertObjectFitToResizeMode(style.objectFit)
style.objectFit != null
? convertObjectFitToResizeMode(style.objectFit)
: null;
const resizeMode =
// $FlowFixMe[prop-missing]
objectFit || props.resizeMode || (style && style.resizeMode) || 'cover';
// $FlowFixMe[prop-missing]
const tintColor = props.tintColor || style.tintColor;
objectFit || props.resizeMode || style.resizeMode || 'cover';
const tintColor = props.tintColor ?? style.tintColor;

if (props.children != null) {
throw new Error(
Expand Down Expand Up @@ -166,7 +169,6 @@ let BaseImage: AbstractImageIOS = React.forwardRef((props, forwardedRef) => {
accessibilityLabel={accessibilityLabel ?? props.alt}
ref={forwardedRef}
style={style}
// $FlowFixMe[incompatible-type]
resizeMode={resizeMode}
tintColor={tintColor}
source={sources}
Expand Down
2 changes: 1 addition & 1 deletion packages/react-native/Libraries/Image/Image.js.flow
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*
* @flow
* @flow strict-local
* @format
*/

Expand Down
15 changes: 8 additions & 7 deletions packages/react-native/Libraries/Image/ImageTypes.flow.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,13 +4,14 @@
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*
* @flow
* @flow strict-local
* @format
*/

import type {RootTag} from '../Types/RootTagTypes';
import type {ResolvedAssetSource} from './AssetSourceResolver';
import type {ImageProps as ImagePropsType} from './ImageProps';
import type {ImageSource} from './ImageSource';
import typeof ImageViewNativeComponent from './ImageViewNativeComponent';
import typeof TextInlineImageNativeComponent from './TextInlineImageNativeComponent';

Expand All @@ -20,29 +21,29 @@ type ImageComponentStaticsIOS = $ReadOnly<{
getSize: (
uri: string,
success: (width: number, height: number) => void,
failure?: (error: any) => void,
failure?: (error: mixed) => void,
) => void,

getSizeWithHeaders(
uri: string,
headers: {[string]: string, ...},
success: (width: number, height: number) => void,
failure?: (error: any) => void,
): any,
failure?: (error: mixed) => void,
): void,

prefetch(url: string): any,
prefetch(url: string): Promise<boolean>,

prefetchWithMetadata(
url: string,
queryRootName: string,
rootTag?: ?RootTag,
): any,
): Promise<boolean>,

queryCache(
urls: Array<string>,
): Promise<{[string]: 'memory' | 'disk' | 'disk/memory', ...}>,

resolveAssetSource(source: any): ?ResolvedAssetSource,
resolveAssetSource(source: ImageSource): ?ResolvedAssetSource,
}>;

type ImageComponentStaticsAndroid = $ReadOnly<{
Expand Down

0 comments on commit 5108ca2

Please sign in to comment.