typescriptreact-nativetouchableopacitypressable

Custom TouchableOpacity from Pressable TypeScript error


Have this custom TouchableOpacity function component, but for the style prop I'm getting a TS error

import { StyleSheet, Pressable, PressableProps, GestureResponderEvent } from 'react-native';

export default function TouchableOpacity(props: PressableProps) {
  const { style, onPress, children } = props;

  return (
    <Pressable
      onPress={(event: GestureResponderEvent) => {
        onPress?.(event);
      }}
      style={({ pressed }) => [style, pressed ? styles.buttonPressedOpacity : null]}>
      {children}
    </Pressable>
  );
}

const styles = StyleSheet.create({
  buttonPressedOpacity: {
    opacity: 0.5,
  },
});

Here is the complete TS complaint:

TS2322: Type '({ pressed }: PressableStateCallbackType) => (StyleProp | ((state: PressableStateCallbackType) => StyleProp<...>) | { ...; })[]' is not assignable to type 'StyleProp | ((state: PressableStateCallbackType) => StyleProp)'.   Type '({ pressed }: PressableStateCallbackType) => (StyleProp | ((state: PressableStateCallbackType) => StyleProp<...>) | { ...; })[]' is not assignable to type '(state: PressableStateCallbackType) => StyleProp'.     Type '(StyleProp | ((state: PressableStateCallbackType) => StyleProp) | { opacity: number; })[]' is not assignable to type 'StyleProp'.       Type '(StyleProp | ((state: PressableStateCallbackType) => StyleProp) | { opacity: number; })[]' is not assignable to type 'RecursiveArray<ViewStyle | Falsy | RegisteredStyle>'.         The types returned by 'pop()' are incompatible between these types.           Type 'StyleProp | ((state: PressableStateCallbackType) => StyleProp) | { opacity: number; }' is not assignable to type 'ViewStyle | Falsy | RegisteredStyle | RecursiveArray<ViewStyle | Falsy | RegisteredStyle> | readonly (ViewStyle | ... 1 more ... | RegisteredStyle<...>)[]'.             Type '(state: PressableStateCallbackType) => StyleProp' is not assignable to type 'ViewStyle | Falsy | RegisteredStyle | RecursiveArray<ViewStyle | Falsy | RegisteredStyle> | readonly (ViewStyle | ... 1 more ... | RegisteredStyle<...>)[]'. index.d.ts(542, 5): The expected type comes from property 'style' which is declared here on type 'IntrinsicAttributes & PressableProps & RefAttributes'


Solution

  • The issue here is the type of style which is received from PressableProps and its concatination with the function you provide to the style prop of Pressable. The type of style is as follows:

    const style: StyleProp<ViewStyle> | ((state: PressableStateCallbackType) => StyleProp<ViewStyle>)
    

    The style is either StyleProp<ViewStyle> or a function that receives a PressableStateCallbackType as an input parameter.

    However, the type of the style prop of Pressable is as follows:

    PressableProps.style?: StyleProp<ViewStyle> | ((state: PressableStateCallbackType) => StyleProp<ViewStyle>)
    

    Thus, the following would work:

    <Pressable
      style={style}>
    </Pressable>
    

    Since the types match.

    However, if you provide the function that receives PressableStateCallbackType yourself and you want to combine this with an additional style, then you have two possibilities.

    You know that style is indeed the mentioned function

    const _style = style as (pressed: PressableStateCallbackType) => StyleProp<ViewStyle>
    
    <Pressable   
      style={(state) => [_style(state), state.pressed && styles.buttonPressedOpacity]}>
    </Pressable>
    

    You know that style is a StyleProp<ViewStyle>

    <Pressable
      style={({pressed}) => [style as StyleProp<ViewStyle>, pressed && styles.buttonPressedOpacity]}>
    </Pressable>
    

    You do not know

    <Pressable
      style={(state) => [typeof style === 'function' ? style(state) : style, state.pressed && styles.buttonPressedOpacity]}>
    </Pressable>
    

    Edit: Actually, if you know the type and it is your custom component, then you could change the typing of the props of TouchableOpacity.