Align ImageStyle overflow prop type and compose function type (#51285)

Summary:
Pull Request resolved: https://github.com/facebook/react-native/pull/51285

The `composeStyles` function should correctly determine the type of the input styles (`ViewStyle`, `ImageStyle`, `TextStyle`) base on the output type:

```ts
const combinedStyle8: StyleProp<ImageStyle> = StyleSheet.compose(
  // ts-expect-error
  composeTextStyle,
  composeTextStyle,
);
```

This diff adds generic type checking for `compose` function and fixes `ImageStyle` overflow prop type which accepted `scroll` property (which wasn't previously accepted in manual types) and which enables type system to distinguish `ImageStyle` from `ViewStyle` and `TextStyle`:

previous:
```ts
overflow?: 'visible' | 'hidden' | 'scroll'
```

current:
```t
overflow?: 'visible' | 'hidden'
```

Changelog:
[Internal]

Reviewed By: huntie

Differential Revision: D74574293

fbshipit-source-id: 751a44f2d3cd43055d93031343995f16ef87b185
This commit is contained in:
Dawid Małecki
2025-05-14 07:44:05 -07:00
committed by Facebook GitHub Bot
parent b817b1a75b
commit 4d0f2b2ca7
5 changed files with 16 additions and 5 deletions
@@ -1002,6 +1002,7 @@ export type ____ImageStyle_InternalCore = $ReadOnly<{
objectFit?: 'cover' | 'contain' | 'fill' | 'scale-down' | 'none',
tintColor?: ____ColorValue_Internal,
overlayColor?: string,
overflow?: 'visible' | 'hidden',
}>;
export type ____ImageStyle_Internal = $ReadOnly<{
@@ -7518,6 +7518,7 @@ export type ____ImageStyle_InternalCore = $ReadOnly<{
objectFit?: \\"cover\\" | \\"contain\\" | \\"fill\\" | \\"scale-down\\" | \\"none\\",
tintColor?: ____ColorValue_Internal,
overlayColor?: string,
overflow?: \\"visible\\" | \\"hidden\\",
}>;
export type ____ImageStyle_Internal = $ReadOnly<{
...____ImageStyle_InternalCore,
+12 -5
View File
@@ -4,19 +4,26 @@
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*
* @flow strict
* @format
*/
import type {
ImageStyle,
TextStyle,
ViewStyle,
} from '../../../Libraries/StyleSheet/StyleSheet';
import type {StyleProp} from '../../../Libraries/StyleSheet/StyleSheetTypes';
/**
* Combines two styles such that `style2` will override any styles in `style1`.
* If either style is null or undefined, the other one is returned without
* allocating an array, saving allocations and enabling memoization.
*/
export default function composeStyles<T1, T2>(
style1: ?T1,
style2: ?T2,
): ?(T1 | T2 | $ReadOnlyArray<T1 | T2>) {
export default function composeStyles<
T: ViewStyle | ImageStyle | TextStyle,
U: T,
V: T,
>(style1: ?StyleProp<U>, style2: ?StyleProp<V>): ?StyleProp<T> {
if (style1 == null) {
return style2;
}
@@ -34,6 +34,7 @@ function StaticViewAndImage(props: Props): React.Node {
<Text>Hello world!</Text>
</View>
</View>
{/* $FlowFixMe - ImageStyle is not compatible with ViewStyle */}
<Image
source={props.imageSource ?? hotdog}
style={[props.style, styles.commonImage]}
@@ -48,6 +48,7 @@ function LayeredImage(props: Props) {
<ImageBackground
source={require('../../assets/rainbow.jpeg')}
style={[styles.backdrop, {width: 200}]}>
{/* $FlowFixMe - ImageStyle is not compatible with ViewStyle */}
<Image
source={require('../../assets/alpha-hotdog.png')}
style={[styles.commonImage, props.style]}