It's not the best solution since consumers of the component can still import the wrong things, but it's a step in the right direction. We metro bundler can find the proper files and we get type safety
Folder
- PlatformImage
- index.tsx
- PlatformImage.android.tsx
- PlatformImage.d.ts
- PlatformImage.ios.tsx
Testing.tsx
import React from 'react';
import { StyleSheet } from 'react-native';
import PlatformImage from '/ui/corex/PlatformImage';
const Testing = () => {
return (
<PlatformImage
style={styles.prefixImage}
borderRadius={styles.prefixImage.borderRadius}
source={{
uri: 'my uri',
priority: 'high',
}}
/>
);
};
const styles = StyleSheet.create({
prefixImage: {
resizeMode: 'contain',
width: '100%',
height: '100%',
borderRadius: 40,
},
});
export default Testing;
PlatformImage.d.ts
import { ImageStyle, StyleProp } from 'react-native';
import { Priority } from 'react-native-fast-image';
/**
* An restricted list of props as we don't currently want to overcomplicate the code by supporting more
* props than we actually need. If we need specific props that are supported by react-native.Image or
* react-native-fast-image, we can add them as we need them.
*/
export interface PlatformImageProps {
testID?: string;
style?: StyleProp<ImageStyle>;
source: {
uri: string;
/**
* Only used in ios
*/
priority: Priority;
};
/**
*
* Only used on android for border radius (since styles.borderRadius doesn't work for android). For ios, specify in styles.
* Ideally the api for this component should be the same for both android & ios,
* but this would mean preventing a styles.borderRadius from being used in ios in favor of using the
* borderRadius prop, but this could be messy to code & maintain.
*/
borderRadius?: number;
resizeMode?: 'contain' | 'cover' | 'stretch' | 'center';
/**
* Invoked when load either succeeds or fails
*/
onLoadEnd?: () => void;
}
declare const PlatformImage: React.ComponentType<PlatformImageProps>;
export default PlatformImage;
index.tsx
import PlatformImage from '/ui/corex/PlatformImage/PlatformImage';
export default PlatformImage;
PlatformImage.android.tsx
import React from 'react';
import { Image } from 'react-native';
import { PlatformImageProps } from '/ui/corex/PlatformImage/PlatformImage';
/**
* Components should never import this directly, but should rather import the index.tsx file
*/
const PlatformImage = ({
testID,
style,
source,
resizeMode = 'cover',
borderRadius,
onLoadEnd,
}: PlatformImageProps) => {
console.log('running android');
return (
<Image
testID={testID}
// todo simon: fix this typescript error
style={style}
resizeMode={resizeMode}
borderRadius={borderRadius}
onLoadEnd={onLoadEnd}
source={source}
/>
);
};
export default PlatformImage;
PlatformImage.ios.tsx
import React from 'react';
import FastImage from 'react-native-fast-image';
import { PlatformImageProps } from '/ui/corex/PlatformImage/PlatformImage';
/**
* Components should never import this directly, but should rather import the index.tsx file
*/
const PlatformImage = ({
testID,
style,
source,
resizeMode = 'cover',
onLoadEnd,
}: PlatformImageProps) => {
console.log('running ios');
return (
<FastImage
testID={testID}
// todo simon: fix this typescript error
style={style}
resizeMode={resizeMode}
onLoadEnd={onLoadEnd}
source={source}
/>
);
};
export default PlatformImage;
The mistake-prone import. Notice how vscode suggests a bunch of imports but we should only ever do import PlatformImage from '/ui/corex/PlatformImage';
